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


17

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

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

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

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

Спасибо


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

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

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


Предупреждение: мне иногда приходилось синглетно сериализировать доступы в многопоточном коде. Так что вы можете проверить это как часть своей стадии профилирования.
— Ричард

Я бы, конечно, не поместил их в одиночный код ... На практике эти константы изменятся в будущем, когда (не если) вы примените свою модель в другом домене. Наличие одного сингла делает тестирование с другими параметрами очень трудным.
— Андре

Они все постоянные. Здесь нет необходимости в синглтоне. Статический класс доступа лучше использовать здесь. Еще лучше было бы статический класс, в котором значения извлекаются из файла конфигурации (поэтому, если ваш конечный пользователь видит ошибку или хочет большей точности, он может настроить файл конфигурации без получения новой сборки).
— Подводное Стив

Синглтоны редко, если вообще, хорошая идея. Внедрение зависимостей - намного более чистое и более гибкое решение. Однако, с простыми константами, я бы сказал, просто держите их где-то в заголовке.
— Mascoj
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.