在库模块和其测试模块之间共享数据

时间:2014-07-05 14:12:23

标签: rust conditional-compilation

我有一个寻路算法库,以及一个测试模块,它以图形方式显示算法所采用的每一步。我们的想法是,应该能够有条件地编译算法的测试或库。因此,算法对象应保持精简,不存储也不返回测试之外不需要的任何数据。

目前我正在使用文本文件作为中间存储,只要它们发生就将步骤写入其中。当搜索完成时,测试模块从那里读取它们。

测试模块是算法模块的子代。

是否有更好的中间存储?有可能获得静态可变载体的一些方法吗?我还阅读了有关任务本地存储的内容,但没有记录好。

编辑:

不幸的是,这样的事情似乎不起作用:

pub struct JumpPointSearch {
    closed_set: Vec<Node>,
    open_set: PriorityQueue<Node>,
    #[cfg(demo)] steps: Vec<(Point2<uint>,Point2<uint>)>
}

impl JumpPointSearch {
    pub fn new() -> JumpPointSearch {
        if cfg!(demo) {
            JumpPointSearch {
                closed_set: Vec::with_capacity(40),
                open_set: PriorityQueue::with_capacity(10),
                steps: Vec::new()
            }
        } else {
            JumpPointSearch { // error: missing field: `steps`
                closed_set: Vec::with_capacity(40),
                open_set: PriorityQueue::with_capacity(10),
            }
        }
    }
}

这也不起作用:

pub struct JumpPointSearch {
    closed_set: Vec<Node>,
    open_set: PriorityQueue<Node>,
    #[cfg(demo)] steps: Vec<(Point2<uint>,Point2<uint>)>
}

impl JumpPointSearch {
    pub fn new() -> JumpPointSearch {
        JumpPointSearch {
            closed_set: Vec::with_capacity(40),
            open_set: PriorityQueue::with_capacity(10),
            #[cfg(demo)] steps: Vec::new() 
            // error: expected ident, found `#`
        }
    }
}

1 个答案:

答案 0 :(得分:1)

对于像这样的条件定义,您需要使用属性表单,而不是宏。那是

#[cfg(demo)]
pub fn new() -> JumpPointSearch {
    JumpPointSearch {
        closed_set: Vec::with_capacity(40),
        open_set: PriorityQueue::with_capacity(10),
        steps: Vec::new()
    }
}
#[cfg(not(demo))]
pub fn new() -> JumpPointSearch {
    JumpPointSearch {
        closed_set: Vec::with_capacity(40),
        open_set: PriorityQueue::with_capacity(10),
    }
}

cfg!宏只会扩展为truefalse,具体取决于配置是否匹配,即两个分支都不会被删除,并且类型检查(等)仍然会发生,这就是为什么它不起作用:其中一个结构初始化与定义不匹配。


但是,我认为有更高级别的方法,它们都非常相似,但要点是始终使JumpPointSearch实现相同,并且只需拥有steps字段更改。

真正的泛型

struct ReleaseSteps;

struct DebugSteps {
    steps: Vec<(Point2<uint>, Point2<uint>)>
}

trait Step {
    fn register_step(&mut self, step: (Point<uint>, Point<uint>));
}

impl Step for ReleaseSteps {
    #[inline(always)] // ensure that this is always 0 overhead
    fn register_step(&mut self, step: (Point<uint>, Point<uint>)) {}
}

impl Step for DebugSteps {
    fn register_step(&mut self, step: (Point<uint>, Point<uint>)) {
        self.steps.push(step)
    }
}

struct JumpPointSearch<St> {
    closed_set: Vec<Node>,
    open_set: PriorityQueue<Node>,
    steps: St
}

impl<St: Step> JumpPointSearch<St> {
    fn new(step: St) -> JumpPointSearch<St> { ... }
}

然后,只要您之前更新steps,只需致电register_step:如果它处于&#34;发布模式&#34;,则会被忽略,如果它处于&#34;调试模式&#34;,它将被注册。

这有一个不幸的缺点,需要仿制药等(可以通过谨慎使用来改善; - cfg例如#[cfg(demo)] impl JumpPointSearch<DebugSteps> { fn new() -> JumpPointSearch<DebugSteps> { ... } } +与#[cfg(not(demo))]对应的ReleaseSteps。 - 默认类型参数,例如struct JumpPointSearch<St = ReleaseSteps> { ... }(这些仍然是实验性的)

这种方法允许您通过在Steps特征中提供更多钩子来轻松扩展诊断(例如,您可以提供记录更昂贵的计算信息的选项,并且具有三个级别{{1 }:release,debug和super-debug)。

Step&#34; generics&#34;

另一个选项是使用上一部分中定义的类型cfg以固定方式选择cfgDebugSteps,即:

ReleaseSteps

如果您不需要可扩展性并且很乐意在编译时进行固定选择,这是我推荐的方法:您根本不需要很多#[cfg(demo)] use Steps = DebugSteps; #[cfg(not(demo))] use Steps = ReleaseSteps; struct JumpPointSearch { closed_set: Vec<Node>, open_set: PriorityQueue<Node>, steps: Steps } 个(只是两个以上,可能还有两个,具体取决于您处理从#[cfg]字段中提取数据的方式),并且您不必抛出大量的泛型。

两个小问题:

  • 使用steps允许use工作,这意味着您可以在需要该类型时编写Steps::new()。 (另外两种可能性存在问题:使用结构字段,即Steps,两者都失败;并且使用#[cfg(demo)] steps: DebugSteps不允许(还)允许#[cfg(demo)] type Steps = DebugSteps;。)
  • 你也可以放弃这个特征的Steps::new()特征,直接Steps方法,即:impl,同样适用于impl ReleaseSteps { fn new() -> ReleaseSteps { ... } fn register_steps(...) {} }