【问题标题】:How to store a boost::quantity with possible different boost::dimension如何使用可能不同的 boost::dimension 存储 boost::quantity
【发布时间】:2013-12-17 09:52:34
【问题描述】:

我正在使用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 是一个合适的解决方案,它的缺点是什么?

【问题讨论】:

    标签: c++ physics boost-units


    【解决方案1】:

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

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

    第一个为接口实现提供了很好的想法。第二部分全面概述了您必须应对的问题。

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

    关于原题:

    • boost::variant 工作得很好(它提供了动态的boost::units 缺失)对于这项工作。此外,它可以开箱即用地序列化。因此,这是一种有效的方法。但它为可以由单个类完成的简单(我不是说微不足道)任务添加了一层抽象。
    • 铸造是通过boost::variant_cast&lt;&gt;而不是dynamic_cast&lt;&gt;实现的。
    • boost::any 可能更容易实现,但序列化变得困难重重。

    【讨论】:

      【解决方案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++98 中完成),同时也表明这在 C++11 中变得更容易(在 C+ 中甚至更简单) +14 decltype(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

      【讨论】:

      • +1 并将其切换为答案。感谢您的帮助,即使我已经解决了一段时间的问题,但它是完整且写得好的。
      • @jlandercy,出于好奇,从长远来看,什么对您有用?这些选项中的任何一个?
      • 我研究时间序列(主要是浓度),必须在报告过程中(在运行时)以不同的单位转换。我自己创建了一个units 类,并使用(每个向量一个实例而不是每个数字一个实例)更新了我的数据集结构。 Units 类沿流程记住转换操作,并在需要时将其应用于数据。所以我确实有运行时间开销,但我把它最小化了。顺便说一句,我现在正在学习 C++ 11,但我确信我不会使用 auto 关键字。无论如何,您的回答再次表明,在 C++ 中解决问题有很多不同的方法。
      • @jlandercy,是的,我也没有提到问题的“容器”(例如向量)方面。然后可能会出现其他解决方案,特别是与内存开销相关的解决方案,因为您可以在容器级别进行类型擦除。
      • 为此我创建了 PhysUnits-RT - 一个 C++98 仅标头库,用于运行时维度分析和单位/数量操作和转换,
      猜你喜欢
      • 2012-12-05
      • 1970-01-01
      • 2013-12-01
      • 1970-01-01
      • 2017-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多