C ++ Mejores prácticas para tratar con muchas constantes, variables en códigos científicos

17

Estoy desarrollando un código para simular el flujo de fluidos con sustancias biológicas presentes en el flujo. Esto implica las ecuaciones estándar de Navier-Stokes acopladas a algunos modelos biológicos adicionales. Hay muchos parámetros / constantes.

He escrito funciones para manejar los cálculos principales, pero un problema que tengo es la gran cantidad de constantes / parámetros de los que dependen estos cálculos. Parece engorroso pasar 10-20 argumentos a una función.

Una alternativa es hacer que todas las constantes sean variables globales, pero sé que esto está mal visto en C ++.

¿Cuál es la forma estándar de manejar muchas entradas a una función? ¿Debo hacer una estructura y pasar eso en su lugar?

Gracias

EternusVia
fuente
77
Si es posible, intente evaluar las constantes en tiempo de compilación utilizando constexpr. Intento incluir la mayoría de estos en un archivo de encabezado separado. Para las variables, he encontrado que una clase separada tiene beneficios, pero a costa de potencialmente más errores porque debe inicializar la clase antes de pasar a la función.
Biswajit Banerjee
3
Esto es difícil de responder correctamente sin algún tipo de muestra de código. ¿Debo hacer una estructura y pasar eso en su lugar? En general, sí, esta es absolutamente la forma habitual de hacerlo. Agrupe los parámetros / constantes por su significado.
Kirill
1
"Una alternativa es hacer que todas las constantes sean variables globales, pero sé que esto está mal visto en C ++" ¿Lo es?
Lightness compite con Monica
1
¿Son realmente, realmente constantes? ¿Qué sucede si desea aplicar su modelo en un dominio diferente? Yo recomendaría ponerlos en una pequeña clase. Eso al menos te da un poco de flexibilidad en el futuro
André
@ André La mayoría de ellos están controlados por el usuario a través de un archivo de parámetros, por lo que estoy de acuerdo en que la solución de clase es la mejor.
EternusVia

Respuestas:

13

Si tiene constantes que no cambiarán antes de las ejecuciones, declarelas en un archivo de encabezado:

//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 razón por la que desearía hacer esto es que permite que el compilador calcule valores constantes antes del tiempo de ejecución, lo cual es bueno si tiene muchos de ellos.

También puede usar una clase simple para pasar valores:

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);
}
Ricardo
fuente
Todas excelentes respuestas, pero la solución de clase funciona mejor para mi situación.
EternusVia
8
Si convierte las variables en globales constexpr, al menos enciérrelas en una namespacepara que no pisen ningún otro símbolo global. Usar una variable global llamada Ges solo pedir problemas.
Wolfgang Bangerth
1
¿Por qué lideras incluir guardias con _? Nunca debe escribir algo que comience con _, corre el riesgo de colisión con los vars del compilador. Deberías estar haciendo algo así ifndef PROJECT_NAME_FILE_NAME_EXTENSION. Además, no estoy seguro de por qué capitalizó las constantes, pero no sus macros de protección de inclusión. En general, desea capitalizar todas las macros, especialmente porque no son sanitarias. Para las constantes, la capitalización no tiene sentido en general . Gestá bien porque es SI, pero mass_earth es más apropiado y debe estar calificado con un espacio de nombres para significar global, es decir constants::mass_earth.
cuando
12

Otra alternativa que puede estar en línea con su línea de pensamiento es usar un espacio de nombres (o espacios de nombres anidados) para agrupar adecuadamente las constantes. Un ejemplo podría ser:

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

Usando la técnica anterior, puede localizar constantes de referencia en algunos archivos y espacios de nombres deseados, haciéndolos más controlados que las variables globales mientras obtiene algunos de los beneficios similares. Cuando usa las constantes, es tan simple como hacer:

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

Si no le gustan las largas cadenas de espacios de nombres anidados, siempre puede acortar las cosas cuando sea necesario utilizando un alias de espacio de nombres:

namespace const_earth = constants::earth;
constexpr double G_times_2 = 2.0*const_earth::G;
spektr
fuente
2
Este es un enfoque, seguido por OpenFOAM , vea un ejemplo aleatorio del código fuente de OpenFOAM . OpenFOAM es una biblioteca de códigos C ++ que implementa el método de volumen finito, que se usa ampliamente en la dinámica de fluidos.
Dohn Joe
1

Una forma en que lo hago es usar singleton.

Cuando inicia su programa, inicia su singleton y lo llena con los datos constantes (probablemente de un archivo de propiedades que tiene para la ejecución). Obtiene esto en cada clase que necesita los valores y simplemente lo usa.

Ashkan
fuente
Advertencia: ocasionalmente he tenido accesos de serialización singletons en código multiproceso. Por lo tanto, es posible que desee verificar esto como parte de su etapa de perfil.
Richard
Ciertamente no los pondría en un singleton ... En la práctica, esas constantes cambiarán en el futuro cuando (no si) aplique su modelo en un dominio diferente. Tenerles un singleton hace que sea muy difícil probar con diferentes parámetros.
André
Todos son constantes. No hay necesidad de un singleton aquí. Una clase de acceso estático es un mejor uso aquí. Aún mejor sería una clase estática donde los valores se extraen de un archivo de configuración (por lo que si su usuario final ve que hay un error o desea más precisión, puede ajustar el archivo de configuración sin obtener una nueva compilación).
Scuba Steve
Los singletons rara vez son, si alguna vez, una buena idea. La inyección de dependencia es una solución mucho más limpia y flexible. Sin embargo, con solo constantes, diría que solo las mantenga constantes en un encabezado en alguna parte.
mascoj