处理科学代码中许多常量,变量的C ++最佳实践


17

我正在开发代码,以使用流体中存在的生物物质来模拟流体流动。这涉及到标准的Navier-Stokes方程以及一些其他的生物学模型。有许多参数/常量。

我已经编写了处理主要计算的函数,但是我遇到的一个问题是这些计算所依赖的大量常量/参数。将10-20个参数传递给一个函数似乎很麻烦。

一种替代方法是使所有常量成为全局变量,但是我知道这在C ++中是不受欢迎的。

处理一个功能的许多输入的标准方法是什么?我应该构造一个结构并通过它吗?

谢谢


7
如果可能,请尝试使用constexpr在编译时评估常量。我尝试将其中大多数包含在单独的头文件中。对于变量,我发现单独的类有好处,但是以潜在的更多错误为代价,因为您必须在传递给函数之前初始化该类。
Biswajit Banerjee,

3
没有某种代码示例,很难正确回答。我应该构造一个结构并通过它吗?通常,是的,这绝对是通常的做法。按参数/常量的含义对其分组。
基里尔

1
“一种选择是使所有常量成为全局变量,但是我知道这在C ++中是不合时宜的”是吗?
莫妮卡(Monica)

1
它们真的是真的常数吗?如果要在其他域中应用模型怎么办?我建议把他们放在小班上。这至少给你的灵活性,一点点的未来
安德烈

@André其中大多数是通过参数文件由用户控制的,这就是为什么我同意类解决方案是最佳的原因。
EternusVia'2

Answers:


13

如果您的常量在运行前不会改变,请在头文件中声明它们:

//constants.hpp
#ifndef PROJECT_NAME_constants_hpp
#define PROJECT_NAME_constants_hpp
namespace constants {
  constexpr double G        = 6.67408e-11;
  constexpr double M_EARTH  = 5.972e24;
  constexpr double GM_EARTH = G*M_EARTH; 
}
#endif

//main.cpp
using namespace constants;
auto f_earth = GM_EARTH*m/r/r;  //Good
auto f_earth = G*M_EARTH*m/r/r; //Also good: compiler probably does math here too

之所以要这样做,是因为它允许编译器在运行时之前提前计算常量值,如果您有很多常量值,那么这很好。

您还可以使用一个简单的类来传递值:

class Params {
 public:
  double a,b,c,d;
  Params(std::string config_file_name){
    //Load configuration here
  }
};

void Foo(const Params &params) {
  ...
}

int main(int argc, char **argv){
  Params params(argv[1]);
  Foo(params);
}

所有很好的答案,但课堂解决方案最适合我的情况。
EternusVia'2

8
如果您将变量设置为全局变量constexpr,则至少将它们括起来,namespace以免它们踩在任何其他全局符号上。使用名为的全局变量G只会带来麻烦。
Wolfgang Bangerth,

1
为什么您要用_来包括后卫?您绝不应该编写以_开头的任何内容,否则有可能与编译器vars冲突。你应该做类似的事情ifndef PROJECT_NAME_FILE_NAME_EXTENSION。另外,不确定为什么要大写常量,但不能包括include宏。您通常希望将所有宏都大写,尤其是因为它们不卫生。对于常量资本没有意义一般G很好,因为它的SI,但mass_earth更合适,并且应使用命名空间进行限定,以表示全局ie constants::mass_earth
WHN

12

可能与您的思路相吻合的另一种选择是使用名称空间(或嵌套名称空间)正确地对常量进行分组。一个示例可能是:

namespace constants {
   namespace earth {
      constexpr double G = 6.67408e-11;
      constexpr double Mass_Earth = 5.972e24;
      constexpr double GM = G*Mass_Earth;
   }// constant properties about Earth

   namespace fluid {
      constexpr double density = 0.999; // g/cm^3
      constexpr double dyn_viscosity = 1.6735; //mPa * s
   }// constants about fluid at 2C

   // ... 

} // end namespace for constants

使用上述技术,您可以将引用常量本地化为某些所需的文件和名称空间,从而使它们比全局变量更受控制,同时获得一些类似的好处。使用常量时,它很简单:

constexpr double G_times_2 = 2.0*constants::earth::G;

如果您不喜欢嵌套名称空间的长链,则始终可以在必要时使用名称空间别名来缩短内容:

namespace const_earth = constants::earth;
constexpr double G_times_2 = 2.0*const_earth::G;

2
这是OpenFOAM遵循的一种方法,请参阅OpenFOAM 源代码的随机示例。OpenFOAM是实现有限体积方法的C ++代码库,该方法广泛用于流体动力学中。
恩·乔

1

我做的一种方法是使用单例。

启动程序时,您将启动单例并用常量数据填充(可能来自运行时使用的属性文件)。您需要使用每个值在每个类中使用它。


警告:我偶尔有单例在多线程代码中序列化访问。因此,您可能需要在概要分析阶段对此进行检查。
理查德

我当然不会将它们放在单例中……实际上,当(而不是如果)将模型应用于其他域时,这些常数将来会更改。让他们成为单身人士使得使用不同的参数进行测试变得非常困难。
安德烈(André),

它们都是常数。这里不需要单身人士。在这里,静态访问器类是更好的用法。更好的方法是从配置文件中提取值的静态类(因此,如果您的最终用户发现错误或需要更高的精度,他们可以在不获取新构建的情况下调整配置文件)。
水肺史蒂夫,

单例很少(如果有的话)不是一个好主意。依赖注入是一种更干净,更灵活的解决方案。但是,仅使用常量,我想说的就是将常量保留在标头中的某个位置。
mascoj
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.