Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I override a constant via a compiler option?

Tags:

rust

Is it possible to define a constant in source code that can be overridden by a compiler flag? That is, something like setting a #define value in the C preprocessor with the -D key=val option to the compiler.

I have read about conditional compilation via the #[cfg(...)] attribute, but that only seems to support booleans. I want to allow the user to set the value of a constant during compilation.

Something like this:

#[from_cfg("max_dimensions")]
const MAX_DIMENSIONS: usize = 100_000;
like image 615
jbaiter Avatar asked May 30 '16 13:05

jbaiter


People also ask

Why can't I override compiler options?

Installation defaults: The default compiler options that were set up when your compiler was installed are in effect for your program unless you override those options. (In some installations, certain compiler options are fixed so that you cannot override them. If you have problems with the default options, contact your system administrator.)

How do I control the compilation of my program?

You can direct and control your compilation by using compiler options or by using compiler-directing statements (compiler directives). Compiler options affect the aspects of your program that are listed in the table below.

How do I declare a compiler constant?

You can declare a compiler constant in the following three ways: Each method produces slightly different results from the other two. First, it is important to understand the mechanics of declaration for each case.

What is the difference between const and override in C++?

override means that the class this is a member function of inherits from a class that contains a virtual function of the same name and signature; this function here is overriding that parent virtual function. const here is a promise that this function won't change any member variables, and won't call any functions that might.


2 Answers

No, you can't define constants (read: const bindings) with a compiler flag. But you can use the env! macro for something similar. It reads some environment variable at compile time.

const MAX_DIMENSIONS_RAW: &'static str = env!("MAX_DIMENSIONS");

Sadly, this returns a string and not an integer. Furthermore we can't yet call arbitrary functions (like parse) at compile time to calculate a constant. You could use lazy_static to achieve something similar:

lazy_static! {
    static ref MAX_DIMENSIONS: usize = MAX_DIMENSIONS_RAW.parse().unwrap();
}

Of course you should add proper error handling. If your user doesn't need to define the environment variable, you can use option_env!.

With this approach, you can pass the setting at build time:

$ MAX_DIMENSIONS=1000 cargo build
like image 59
Lukas Kalbertodt Avatar answered Sep 27 '22 19:09

Lukas Kalbertodt


Building on Lukas Kalbertodt's answer, you can get the environment variable as a constant number with some extra indirection, namely by using a build script.

build.rs

use std::{env, fs::File, io::Write, path::Path};

fn main() {
    let out_dir = env::var("OUT_DIR").expect("No out dir");
    let dest_path = Path::new(&out_dir).join("constants.rs");
    let mut f = File::create(&dest_path).expect("Could not create file");

    let max_dimensions = option_env!("MAX_DIMENSIONS");
    let max_dimensions = max_dimensions
        .map_or(Ok(10_000), str::parse)
        .expect("Could not parse MAX_DIMENSIONS");

    write!(&mut f, "const MAX_DIMENSIONS: usize = {};", max_dimensions)
        .expect("Could not write file");
    println!("cargo:rerun-if-env-changed=MAX_DIMENSIONS");
}

main.rs

include!(concat!(env!("OUT_DIR"), "/constants.rs"));

fn main() {
    println!("The value is {} ({})", MAX_DIMENSIONS, MAX_DIMENSIONS + 1);
}
$ cargo run
The value is 10000 (10001)

$ MAX_DIMENSIONS=17 cargo run
The value is 17 (18)

$ MAX_DIMENSIONS=1 cargo run
The value is 1 (2)
like image 28
Shepmaster Avatar answered Sep 27 '22 21:09

Shepmaster