Meilleures pratiques C ++ pour gérer de nombreuses constantes, variables dans les codes scientifiques

17

Je développe un code pour simuler l'écoulement des fluides avec des substances biologiques présentes dans l'écoulement. Cela implique les équations de Navier-Stokes standard couplées à certains modèles biologiques supplémentaires. Il existe de nombreux paramètres / constantes.

J'ai écrit des fonctions pour gérer les principaux calculs, mais un problème que j'ai est le grand nombre de constantes / paramètres dont dépendent ces calculs. Il semble lourd de transmettre 10 à 20 arguments à une fonction.

Une alternative est de rendre toutes les constantes variables globales, mais je sais que cela est mal vu en C ++.

Quelle est la manière standard de gérer de nombreuses entrées d'une fonction? Dois-je faire une structure et la passer à la place?

Je vous remercie

EternusVia
la source
7
Si c'est possible, essayez de faire évaluer les constantes au moment de la compilation à l'aide de constexpr. J'essaie d'inclure la plupart de ceux-ci dans un fichier d'en-tête séparé. Pour les variables, j'ai trouvé qu'une classe séparée a des avantages, mais au prix de potentiellement plus de bugs car il faut initialiser la classe avant de passer dans la fonction.
Biswajit Banerjee
3
Il est difficile de répondre correctement sans une sorte d'échantillon de code. Dois-je faire une structure et la passer à la place? En général, oui, c'est absolument la voie à suivre. Regroupez les paramètres / constantes par leur signification.
Kirill
1
"Une alternative est de rendre toutes les constantes variables globales, mais je sais que cela est mal vu en C ++" Est-ce?
Courses de légèreté avec Monica le
1
Sont-ils vraiment, vraiment constants? Et si vous souhaitez appliquer votre modèle dans un autre domaine? Je recommanderais de les mettre dans une petite classe. Cela vous donne au moins un peu de flexibilité à l'avenir
André
@ André La plupart d'entre eux sont contrôlés par l'utilisateur via un fichier de paramètres, c'est pourquoi je conviens que la solution de classe est la meilleure.
EternusVia

Réponses:

13

Si vous avez des constantes qui ne changeront pas avant les exécutions, déclarez-les dans un fichier d'en-tête:

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

La raison pour laquelle vous voudriez faire cela est qu'elle permet au compilateur de calculer les valeurs constantes avant l'exécution, ce qui est bien si vous en avez beaucoup.

Vous pouvez également utiliser une classe simple pour transmettre des valeurs:

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
la source
Toutes les bonnes réponses, mais la solution de classe fonctionne le mieux pour ma situation.
EternusVia
8
Si vous rendez les variables globales constexpr, au moins les enfermer dans un namespaceafin qu'elles ne marchent pas sur d'autres symboles globaux. L'utilisation d'une variable globale appelée Gest juste un problème.
Wolfgang Bangerth
1
Pourquoi dirigez-vous des gardes avec _? Vous ne devez jamais écrire quoi que ce soit qui commence par _, vous risquez une collision avec les variables du compilateur. Vous devriez faire quelque chose comme ça ifndef PROJECT_NAME_FILE_NAME_EXTENSION. De plus, vous ne savez pas pourquoi vous avez capitalisé les constantes, mais pas vos macros de garde d'inclusion. Vous souhaitez généralement capitaliser toutes les macros, notamment parce qu'elles ne sont pas sanitaires. Pour les constantes, la capitalisation n'a pas de sens en général . Gest bien parce que son SI, mais mass_earth est plus approprié, et devrait être qualifié avec un espace de noms pour signifier global ie constants::mass_earth.
quand le
12

Une autre alternative qui peut être en ligne avec votre réflexion est d'utiliser un espace de noms (ou des espaces de noms imbriqués) pour regrouper correctement les constantes. Un exemple pourrait être:

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

En utilisant la technique ci-dessus, vous pouvez localiser les constantes de référence dans certains fichiers et espaces de noms souhaités, ce qui les rend plus contrôlés que les variables globales tout en bénéficiant de certains avantages similaires. Lorsque vous utilisez les constantes, c'est aussi simple que de faire:

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

Si vous n'aimez pas les longues chaînes d'espaces de noms imbriqués, vous pouvez toujours raccourcir les choses si nécessaire en utilisant un alias d'espace de noms:

namespace const_earth = constants::earth;
constexpr double G_times_2 = 2.0*const_earth::G;
spektr
la source
2
Il s'agit d'une approche, qui est suivie par OpenFOAM , voir un exemple aléatoire du code source d'OpenFOAM . OpenFOAM est une bibliothèque de code C ++ implémentant la méthode des volumes finis, qui est largement utilisée en dynamique des fluides.
Dohn Joe
1

Une façon que je fais est d'utiliser singleton.

Lorsque vous démarrez votre programme, vous lancez votre singleton et le remplissez avec les données constantes (probablement à partir d'un fichier de propriétés que vous avez pour l'exécution). Vous obtenez cela dans chaque classe dont vous avez besoin des valeurs et utilisez-le simplement.

Ashkan
la source
Avertissement: J'ai parfois eu des singletons sérialisant des accès en code multi-thread. Donc, vous voudrez peut-être vérifier cela dans le cadre de votre étape de profilage.
Richard
Je ne les mettrais certainement pas dans un singleton ... En pratique, ces constantes changeront à l'avenir lorsque (pas si) vous appliquerez votre modèle dans un domaine différent. Leur avoir un singleton rend très difficile le test avec différents paramètres.
André
Ce sont toutes des constantes. Il n'y a pas besoin ici d'un singleton. Une classe d'accesseur statique est une meilleure utilisation ici. Encore mieux serait une classe statique où les valeurs sont extraites d'un fichier de configuration (donc si votre utilisateur final voit qu'il y a une erreur, ou veut plus de précision, il peut ajuster le fichier de configuration sans obtenir une nouvelle construction).
Scuba Steve
Les singletons sont rarement, voire jamais, une bonne idée. L'injection de dépendance est une solution beaucoup plus propre et plus flexible. Cependant, avec juste des constantes, je dirais simplement de les conserver quelque part dans un en-tête.
mascoj