C ++ Кращі практики роботи з багатьма константами, змінними в наукових кодах


17

Я розробляю код для моделювання потоку рідини з біологічними речовинами, присутніми в потоці. Це стосується стандартних рівнянь Нав'є-Стокса, пов'язаних з деякими додатковими біологічними моделями. Існує багато параметрів / констант.

У мене є написані функції для обробки основних обчислень, але проблема, з якою у мене виникає, полягає у великій кількості констант / параметрів, від яких залежать ці обчислення. Здається, громіздко передавати 10-20 аргументів функції.

Однією з альтернатив є створення всіх констант глобальними змінними, але я знаю, що це нахмуриться в C ++.

Який стандартний спосіб обробки багатьох входів у функцію? Чи повинен я зробити структуру і передати це замість цього?

Дякую


7
Якщо це можливо, спробуйте оцінити константи під час компіляції за допомогою constexpr. Я намагаюся включити більшість із них у окремий файл заголовка. Щодо змінних, я виявив, що окремий клас має переваги, але ціною на потенційно більше помилок, оскільки вам доведеться ініціалізувати клас перед тим, як перейти до функції.
Biswajit Banerjee

3
На це важко правильно відповісти без якогось зразка коду. Чи повинен я зробити структуру і передати це замість цього? Загалом, так, це абсолютно звичайний шлях. Згрупуйте параметри / константи за їх значенням.
Кирило

1
"Одна з альтернатив - зробити всі константи глобальними змінними, але я знаю, що це нахмуриться в C ++" Це так?
Гонки легкості з Монікою

1
Вони справді, справді константи? Що робити, якщо ви хочете застосувати свою модель в іншому домені? Я б рекомендував розмістити їх у невеликому класі. Що принаймні дає вам трохи гнучкості в майбутньому
Андре

@ André Більшість з них керуються користувачем через файл параметрів, тому я погоджуюся, що рішення класу є найкращим.
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
Чому ви вводите охоронців з _? Ніколи не слід писати нічого, що починається з _, ви ризикуєте зіткнутися з компілятором vars. Ви повинні робити щось подібне ifndef PROJECT_NAME_FILE_NAME_EXTENSION. Крім того, не впевнені, чому ви використовуєте великі літери константи, але не включаєте макроси охорони. Як правило, ви хочете використовувати великі літери всіх макросів, тим більше, що вони не є санітарними. Для постійних капіталізація не має сенсу взагалі . Gце добре, тому що його SI, але mass_earth є більш підходящим, і його слід кваліфікувати з простором імен для позначення глобального, тобто constants::mass_earth.
WHN

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 - це бібліотека кодів С ++, що реалізує метод кінцевого обсягу, який широко використовується в динаміці рідини.
Dohn Joe

1

Один із способів, який я роблю, - це використовувати однотонний.

Коли ви запускаєте програму, ви ініціюєте синглтон і заповнюєте його постійними даними (можливо, з файлу властивостей, який у вас є для запуску). Ви отримуєте це в кожному класі, який вам потрібні, і просто використовуєте його.


Попередження: Іноді у мене є одиночні серіалізатори доступу в багатопотоковий код. Тож ви можете перевірити це як частину свого етапу профілювання.
Річард

Я, безумовно, не ставлю їх в одиночку ... На практиці ці константи будуть змінюватися в майбутньому, коли (якщо не) ви застосуєте свою модель в іншій області. Маючи їх однотонним, дуже важко потім тестувати різні параметри.
Андре

Вони всі константи. Тут немає необхідності в одиночному. Тут краще використовувати статичний клас аксесуарів. Ще краще було б статичний клас, у якому значення витягуються з конфігураційного файлу (тому, якщо ваш кінцевий користувач бачить помилку чи хоче більш точності, він може налаштувати файл конфігурації, не отримуючи нової збірки).
Scuba Steve

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