我正在使用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::variant
和访客模式没有信心。
我的问题如下:
dynamic_cast
是其中之一吗?单位转换不会经常发生,只有少数数据受到关注。boost::variant
是一个合适的解决方案,它的缺点是什么?我一直在考虑这个问题并得出以下结论:
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
更深入地解决我的问题,我读了两篇提供解决方案的文章:
第一个给出了接口实现的好主意。第二部分概述了您必须应对的内容。
我记住,boost::units
是编译时维度一致性的完整而有效的方法,在运行时没有开销。无论如何,对于涉及尺寸变化的运行时尺寸一致性,您需要一个boost::units
不提供的动态结构。所以我就是这样:设计一个完全符合我需求的单元类。要做更多的工作,最终会更满意......
关于原始问题:
boost::variant
工作得很好(它提供了动态的boost::units
缺失)。此外,它可以开箱即用。因此,这是一种有效的方法。但是它为一个简单的方法添加了一层抽象 - 我不是说琐碎的 - 可以由一个类完成的任务。boost::variant_cast<>
而不是dynamic_cast<>
实现的。boost::any
可能更容易实现,但序列化变得很难。