C ++ Лучшие практики для работы со многими константами, переменными в научных кодах

17

Я разрабатываю код для моделирования потока жидкости с биологическими веществами, присутствующими в потоке. Это включает в себя стандартные уравнения Навье-Стокса, связанные с некоторыми дополнительными биологическими моделями. Есть много параметров / констант.

Я написал функции для обработки основных вычислений, но проблема, с которой я сталкиваюсь, заключается в большом количестве констант / параметров, от которых зависят эти вычисления. Кажется неудобным передавать 10-20 аргументов в функцию.

Одна альтернатива - сделать все константы глобальными переменными, но я знаю, что это не одобряется в C ++.

Каков стандартный способ обработки множества входных данных для функции? Должен ли я сделать структуру и передать вместо этого?

Спасибо

EternusVia
источник
7
Если это возможно, попробуйте оценить константы во время компиляции с помощью constexpr. Я пытаюсь включить большинство из них в отдельный заголовочный файл. Что касается переменных, я обнаружил, что отдельный класс имеет преимущества, но за счет потенциально большего количества ошибок, потому что вы должны инициализировать класс перед передачей в функцию.
Бисваджит Банерджи
3
На это трудно ответить правильно без какого-либо примера кода. Должен ли я сделать структуру и передать вместо этого? В общем, да, это абсолютно обычный путь. Сгруппируйте параметры / константы по их значению.
Кирилл
1
«Один из вариантов, чтобы сделать все постоянные глобальные переменные, но я знаю , что это неодобрением в C ++» Является ли это?
Легкость гонки с Моникой
1
Они действительно, действительно постоянны? Что если вы хотите применить свою модель в другом домене? Я бы порекомендовал поместить их в небольшой класс. Это, по крайней мере, дает вам немного гибкости в будущем
Андре
@ Андре Большинство из них управляются пользователем через файл параметров, поэтому я бы согласился, что решение класса лучше.
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. Кроме того, вы не знаете, почему вы использовали заглавные буквы в константах, но не в макросах include guard. Как правило, вы хотите использовать все макросы с большой буквы, особенно потому, что они не являются санитарными. Для постоянных капитализация не имеет смысла вообще . Gэто хорошо , потому что его SI, но mass_earth является более подходящим, и должны быть квалифицированы с пространством имен для обозначения глобального ИЭ constants::mass_earth.
19
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

Один из способов, который я делаю, - это использование синглтона.

Когда вы запускаете вашу программу, вы запускаете свой синглтон и заполняете его постоянными данными (вероятно, из файла свойств, который вы имеете для запуска). Вы получаете это в каждом классе, в котором вам нужны значения, и просто используете его.

Ashkan
источник
Предупреждение: мне иногда приходилось синглетно сериализировать доступы в многопоточном коде. Так что вы можете проверить это как часть своей стадии профилирования.
Ричард
Я бы, конечно, не поместил их в одиночный код ... На практике эти константы изменятся в будущем, когда (не если) вы примените свою модель в другом домене. Наличие одного сингла делает тестирование с другими параметрами очень трудным.
Андре
Они все постоянные. Здесь нет необходимости в синглтоне. Статический класс доступа лучше использовать здесь. Еще лучше было бы статический класс, в котором значения извлекаются из файла конфигурации (поэтому, если ваш конечный пользователь видит ошибку или хочет большей точности, он может настроить файл конфигурации без получения новой сборки).
Подводное Стив
Синглтоны редко, если вообще, хорошая идея. Внедрение зависимостей - намного более чистое и более гибкое решение. Однако, с простыми константами, я бы сказал, просто держите их где-то в заголовке.
Mascoj