C ++ Best Practices für den Umgang mit vielen Konstanten und Variablen in wissenschaftlichen Codes

17

Ich entwickle einen Code zur Simulation des Flüssigkeitsflusses mit im Fluss vorhandenen biologischen Substanzen. Dies beinhaltet die standardmäßigen Navier-Stokes-Gleichungen, die mit einigen zusätzlichen biologischen Modellen gekoppelt sind. Es gibt viele Parameter / Konstanten.

Ich habe Funktionen geschrieben, um die Hauptberechnungen zu handhaben, aber ein Problem, das ich habe, ist die große Anzahl von Konstanten / Parametern, von denen diese Berechnungen abhängen. Es scheint umständlich, 10-20 Argumente an eine Funktion zu übergeben.

Eine Alternative besteht darin, alle Konstanten zu globalen Variablen zu machen, aber ich weiß, dass dies in C ++ verpönt ist.

Wie werden normalerweise viele Eingaben für eine Funktion verarbeitet? Soll ich stattdessen eine Struktur erstellen und diese übergeben?

Vielen Dank

EternusVia
quelle
7
Wenn es möglich ist, versuchen Sie, die Konstanten beim Kompilieren mit constexpr auswerten zu lassen. Ich versuche, die meisten davon in eine separate Header-Datei aufzunehmen. Bei Variablen habe ich festgestellt, dass eine separate Klasse Vorteile hat, jedoch auf Kosten potenziell weiterer Fehler, da Sie die Klasse vor der Übergabe an die Funktion initialisieren müssen.
Biswajit Banerjee
3
Dies ist ohne eine Art Codebeispiel schwer zu beantworten. Soll ich stattdessen eine Struktur erstellen und diese übergeben? Im Allgemeinen ist dies der absolut übliche Weg. Gruppieren Sie die Parameter / Konstanten nach ihrer Bedeutung.
Kirill
1
"Eine Alternative besteht darin, alle Konstanten zu globalen Variablen zu machen, aber ich weiß, dass dies in C ++ verpönt ist."
Leichtigkeit Rennen mit Monica
1
Sind sie wirklich wirklich Konstanten? Was ist, wenn Sie Ihr Modell in einer anderen Domäne anwenden möchten? Ich würde empfehlen, sie in eine kleine Klasse zu stecken. Das gibt Ihnen zumindest ein bisschen Flexibilität in der Zukunft
André
@ André Die meisten von ihnen werden vom Benutzer über eine Parameterdatei gesteuert, weshalb ich der Meinung bin, dass die Klassenlösung die beste ist.
EternusVia

Antworten:

13

Wenn Sie Konstanten haben, die sich vor dem Ausführen nicht ändern, deklarieren Sie diese in einer Header-Datei:

//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

Der Grund, warum Sie dies tun möchten, besteht darin, dass der Compiler vor der Laufzeit konstante Werte berechnen kann. Dies ist gut, wenn Sie viele davon haben.

Sie können auch eine einfache Klasse verwenden, um Werte weiterzugeben:

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);
}
Richard
quelle
Alles gute Antworten, aber die Klassenlösung passt am besten zu meiner Situation.
EternusVia
8
Wenn Sie die Variablen zu globalen Variablen constexprmachen, schließen Sie sie mindestens in ein ein, namespacedamit sie nicht auf andere globale Symbole treten. Die Verwendung einer aufgerufenen globalen Variablen Gist nur ein Problem.
Wolfgang Bangerth
1
Warum führen Sie mit _ auch Wachen? Sie sollten niemals etwas schreiben, das mit _ beginnt, da dies zu Kollisionen mit Compilervars führen kann. Sie sollten so etwas tun ifndef PROJECT_NAME_FILE_NAME_EXTENSION. Sie sind sich auch nicht sicher, warum Sie Konstanten großgeschrieben haben, nicht jedoch Ihre Include-Guard-Makros. Sie möchten im Allgemeinen alle Makros in Großbuchstaben schreiben, vor allem, weil sie nicht hygienisch sind. Für Konstanten ist die Großschreibung im Allgemeinen nicht sinnvoll . Gist in Ordnung, weil sein SI, aber mass_earth ist geeigneter und sollte mit einem Namespace qualifiziert werden, um global zu kennzeichnen, dh constants::mass_earth.
Uhr
12

Eine andere Alternative, die Ihrem Gedankengang entsprechen könnte, besteht darin, einen Namespace (oder verschachtelte Namespaces) zu verwenden, um Konstanten richtig zu gruppieren. Ein Beispiel könnte sein:

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

Mit der oben beschriebenen Technik können Sie Referenzkonstanten in einigen gewünschten Dateien und Namespaces lokalisieren, um sie kontrollierter als globale Variablen zu machen und einige ähnliche Vorteile zu erzielen. Wenn Sie die Konstanten verwenden, ist das ganz einfach:

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

Wenn Sie lange Ketten verschachtelter Namespaces nicht mögen, können Sie die Dinge bei Bedarf jederzeit verkürzen, indem Sie einen Namespace-Alias ​​verwenden:

namespace const_earth = constants::earth;
constexpr double G_times_2 = 2.0*const_earth::G;
spektr
quelle
2
Dies ist ein Ansatz, dem OpenFOAM folgt , siehe ein zufälliges Beispiel des OpenFOAM- Quellcodes . OpenFOAM ist eine C ++ - Codebibliothek, die die in der Fluiddynamik weit verbreitete Methode des endlichen Volumens implementiert.
Dohn Joe
1

Eine Möglichkeit, die ich tue, ist die Verwendung von Singleton.

Wenn Sie Ihr Programm starten, initiieren Sie Ihren Singleton und füllen ihn mit den konstanten Daten (wahrscheinlich aus einer Eigenschaftendatei, die Sie für den Lauf haben). Sie erhalten dies in jeder Klasse, die Sie die Werte benötigen und verwenden Sie es einfach.

Ashkan
quelle
Warnung: Ich hatte gelegentlich Singletons, die Zugriffe in Multithread-Code serialisierten. Vielleicht möchten Sie dies im Rahmen Ihrer Profilerstellung überprüfen.
Richard
Ich würde sie mit Sicherheit nicht in einen Singleton setzen ... In der Praxis werden sich diese Konstanten in Zukunft ändern, wenn Sie Ihr Modell in einer anderen Domäne anwenden (nicht wenn). Wenn Sie einen Singleton haben, ist es sehr schwierig, ihn mit verschiedenen Parametern zu testen.
André
Das sind alles Konstanten. Hier ist kein Singleton erforderlich. Eine statische Accessor-Klasse ist hier eine bessere Verwendung. Noch besser wäre eine statische Klasse, bei der die Werte aus einer Konfigurationsdatei abgerufen werden (wenn der Endbenutzer also einen Fehler feststellt oder mehr Genauigkeit wünscht, kann er die Konfigurationsdatei anpassen, ohne einen neuen Build zu erhalten).
Scuba Steve
Singletons sind selten, wenn überhaupt, eine gute Idee. Die Abhängigkeitsinjektion ist eine viel sauberere und flexiblere Lösung. Bei nur Konstanten würde ich jedoch sagen, dass diese Konstanten irgendwo in einem Header bleiben.
Mascoj