2

我正在使用boost::units库来强制科学项目中的物理一致性。我已经阅读并尝试了 boost 文档中的几个示例。我能够创建我的尺寸、单位和数量。我做了一些微积分,效果很好。这正是我所期望的,除了...

在我的项目中,我处理基于六个维度的具有多个不同单位(温度、浓度、密度等)的时间序列。为了允许安全和简单的单位转换,我想为每个通道类添加一个成员,表示时间序列的维度和单位。而且,数据处理(导入、转换等)是用户驱动的,因此是动态的。

我的问题如下,由于boost::units结构的原因,同质系统中的数量但具有不同的维度有不同的类型。因此,您不能直接声明成员,例如:

boost::units::quantity channelUnits;

编译器将声称您必须使用模板 V 形指定尺寸。但如果这样做,您将无法存储不同类型的数量(例如具有不同维度的数量)。

然后,我查找boost::units::quantity声明以了解是否有可以以多态方式使用的基类。但是我没有找到它,相反我发现它boost::units大量使用了模板元编程,这不是问题,但并不完全符合我的动态需求,因为一切都是在编译时而不是在运行时解决的。

经过更多阅读,我尝试将不同的数量包装在一个boost::variant对象中(很高兴第一次见到它)。

typedef boost::variant<
   boost::units::quantity<dim1>,
   ...
> channelUnitsType;
channelUnitsType channelUnits;

我进行了一些测试,它似乎工作。但我对boost::variantvisitor-pattern没有信心。

我的问题如下:

  • 是否有另一种 - 也许是最好的 - 方法来进行运行时类型解析?
  • dynamic_cast其中之一吗?单位转换不会经常发生,只有很少的数据需要关注。
  • 如果boost::variant是一个合适的解决方案,它的缺点是什么?
4

2 回答 2

3

深入研究我的问题,我阅读了两篇提供解决方案的文章:

  • Kostadin Damevski在科学组件软件的接口中表达测量单位
  • Lingxiao Jiang一种用于验证 C 程序的维度单位正确性的实用类型系统

第一个为接口实现提供了很好的想法。第二个完整概述了您必须处理的内容。

我记住这boost::units是一种在编译时实现维度一致性的完整且有效的方法,而在运行时没有开销。无论如何,对于涉及维度更改的运行时维度一致性,您确实需要一个boost::units不提供的动态结构。所以我在这里:设计一个完全符合我需要的单元类。更多的工作要做,更多的满足在最后......

关于原始问题:

  • boost::variant这项工作效果很好(它提供了boost::units缺失的动态)。此外,它可以开箱即用地序列化。因此,这是一种有效的方法。但它为一个简单的——我不是说微不足道的——可以由单个类完成的任务添加了一层抽象。
  • 铸造是通过boost::variant_cast<>代替来实现的dynamic_cast<>
  • boost::any可能更容易实现,但序列化变得困难重重。
于 2013-12-30T13:31:08.683 回答
2

我一直在思考这个问题并得出以下结论:

1. 实现类型擦除(优点:好的接口,缺点:内存开销)

看起来不可能在没有开销的情况下存储具有共同维度的一般数量,这违反了库的设计原则之一。即使是类型擦除也无济于事。

2. 实现可转换类型(优点:良好的接口,缺点:操作开销)

我看到的没有存储开销的唯一方法是选择一个传统的(可能是隐藏的)系统,所有单位都可以在其中相互转换。没有内存开销,但在几乎所有对值的查询中都有乘法开销,大量的转换和一些高指数精度的松散(想想从 avogadro 数到 10 次方的转换)。

3. 允许隐式转换(优点:好的接口,缺点:更难调试,意外的操作开销)

另一种选择,主要是在实际方面缓解问题是允许在接口级别进行隐式转换,请参见此处:https ://groups.google.com/d/msg/boost-devel-archive/JvA5W9OETt8/5fMwXWuCdDsJ

4. 模板/通用代码(优点:没有运行时或内存开销,概念上正确,哲学遵循库,缺点:更难调试,丑陋的接口,可能的代码膨胀,到处都有很多模板参数)

如果你问库设计者,他们可能会告诉你你需要让你的函数通用。这是可能的,但它使代码复杂化。例如:

template<class Length>
auto square(Length l) -> decltype(l*l){return l*l;}

我使用 C++11 来简化这里的示例(可以C++98decltype(auto).

我知道这不是您想到的代码类型,但它与库的设计是一致的。您可能会想,我如何将此功能限制为物理长度而不是其他东西?好吧,答案是你不需要这样做,但是如果你坚持,在最坏的情况下......

template<class Length, typename std::enable_if<std::is_same<typename get_dimension<Lenght>::type, boost::units::length_dimension>::value>::type>
auto square(Length l) -> decltype(l*l){return l*l;}

(在更好的情况下decltype将完成 SFINAE 的工作。)

在我看来,选项 4. 并且可能与 3. 结合使用是最优雅的方式。


参考:

https://www.boost.org/doc/libs/1_69_0/boost/units/get_dimension.hpp

于 2014-08-10T05:40:34.577 回答