科学コード内の多くの定数、変数を扱うためのC ++ベストプラクティス


17

流れの中に存在する生物学的物質を使用して、流れをシミュレートするコードを開発しています。これには、いくつかの追加の生物学的モデルに結合された標準のナビエ・ストークス方程式が含まれます。多くのパラメーター/定数があります。

私は主要な計算を処理する関数を作成しましたが、私が抱えている問題は、これらの計算が依存する多数の定数/パラメーターです。関数に10〜20個の引数を渡すのは面倒です。

1つの代替方法は、すべての定数をグローバル変数にすることですが、これはC ++では嫌われていることは知っています。

関数への多くの入力を処理する標準的な方法は何ですか?構造体を作成し、代わりに渡す必要がありますか?

ありがとうございました


7
可能であれば、constexprを使用してコンパイル時に定数を評価してください。これらのほとんどを別のヘッダーファイルに含めるようにします。変数については、別のクラスに利点があることを発見しましたが、関数に渡す前にクラスを初期化する必要があるため、潜在的にバグが増える可能性があります。
Biswajitバネルジー

3
これは、なんらかのコードサンプルがなければ適切に答えることは困難です。構造体を作成し、代わりに渡す必要がありますか?一般的に、はい、これは絶対に通常の方法です。パラメータ/定数を意味ごとにグループ化します。
キリル

1
「1つの代替方法は、すべての定数をグローバル変数にすることですが、これはC ++で嫌われていることは知っています
モニカとの軽さのレース

1
彼らは本当に、本当に定数ですか?モデルを別のドメインに適用する場合はどうなりますか?それらを小さなクラスに入れることをお勧めします。少なくとも将来的には少し柔軟性が得られます
アンドレ

@Andréそれらのほとんどは、パラメータファイルを介してユーザーが制御するため、クラスソリューションが最適であることに同意します。
EternusVia

回答:


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

8
変数をグローバル変数にする場合constexprは、少なくとも変数をで囲みnamespace、他のグローバルシンボルを踏まないようにします。と呼ばれるグローバル変数を使用するとG、単にトラブルを招きます。
ヴォルフガングバンガース

1
なぜ_を含むインクルードガードをリードするのですか?_で始まるものを決して書かないでください。コンパイラ変数と衝突する危険があります。あなたのようなことをするべきifndef PROJECT_NAME_FILE_NAME_EXTENSIONです。また、なぜ定数を大文字にしたのかはわかりませんが、インクルードガードマクロはわかりません。特に衛生的ではないため、すべてのマクロを大文字にする必要があります。定数の場合、大文字化は一般的に意味がありません。 GSIで問題ありませんが、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

私が行う方法の1つは、シングルトンを使用することです。

プログラムを開始すると、シングルトンを開始し、定数データ(おそらく実行用に持っているプロパティファイルから)を入力します。値を必要とするすべてのクラスでこれを取得し、それを使用します。


警告:マルチスレッドコードでアクセスをシリアル化するシングルトンが時々ありました。したがって、プロファイリング段階の一部としてこれを確認することをお勧めします。
リチャード

私は確かにそれらをシングルトンに入れないでしょう...実際には、これらの定数は、モデルを別のドメインに適用する場合(しない場合)に将来変更されます。それらをシングルトンにすると、異なるパラメーターでテストすることが非常に難しくなります。
アンドレ

それらはすべて定数です。ここにはシングルトンは必要ありません。ここでは、静的アクセサクラスの使用が適しています。さらに良いのは、構成ファイルから値が取得される静的クラスです(したがって、エンドユーザーが間違いを見つけたり、より正確にしたい場合は、新しいビルドを取得せずに構成ファイルを調整できます)。
スキューバスティーブ

シングルトンはめったに、めったに良いアイデアではありません。依存性注入は、はるかにクリーンで柔軟なソリューションです。ただし、定数だけでは、ヘッダーのどこかに定数を保持するだけです。
mascoj
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.