C ++ Najlepsze praktyki postępowania z wieloma stałymi, zmiennymi w kodach naukowych


17

Opracowuję kod do symulacji przepływu płynu z obecnymi w nim substancjami biologicznymi. Dotyczy to standardowych równań Naviera-Stokesa połączonych z niektórymi dodatkowymi modelami biologicznymi. Istnieje wiele parametrów / stałych.

Napisałem funkcje do obsługi głównych obliczeń, ale mam problem z dużą liczbą stałych / parametrów, od których zależą te obliczenia. Przekazanie funkcji 10-20 argumentów wydaje się kłopotliwe.

Jedną z możliwości jest uczynienie wszystkich stałych zmiennymi globalnymi, ale wiem, że w C ++ jest to niezadowolone.

Jaki jest standardowy sposób obsługi wielu danych wejściowych do funkcji? Czy zamiast tego powinienem utworzyć strukturę i przekazać ją?

Dziękuję Ci


7
Jeśli to możliwe, spróbuj oszacować stałe w czasie kompilacji za pomocą constexpr. Staram się zawrzeć większość z nich w osobnym pliku nagłówkowym. W przypadku zmiennych odkryłem, że osobna klasa ma zalety, ale kosztem potencjalnie większej liczby błędów, ponieważ musisz zainicjować klasę przed przejściem do funkcji.
— Biswajit Banerjee

3
Trudno jest odpowiedzieć poprawnie bez jakiejś próbki kodu. Czy zamiast tego powinienem utworzyć strukturę i przekazać ją? Ogólnie rzecz biorąc, tak, jest to absolutnie zwykła droga. Pogrupuj parametry / stałe według ich znaczenia.
— Kirill

1
„Jedną z możliwości jest uczynienie wszystkich stałych zmiennymi globalnymi, ale wiem, że w C ++ jest to niezadowolone” .
— Wyścigi lekkości z Moniką

1
Czy są naprawdę, naprawdę stałe? Co jeśli chcesz zastosować swój model w innej domenie? Poleciłbym umieszczenie ich w małej klasie. To przynajmniej daje ci trochę elastyczności w przyszłości
— André

@ André Większość z nich jest kontrolowana przez użytkownika za pomocą pliku parametrów, dlatego zgodziłbym się, że rozwiązanie klasowe jest najlepsze.
— EternusVia

Odpowiedzi:


13

Jeśli masz stałe, które nie zmienią się przed uruchomieniem, zadeklaruj je w pliku nagłówkowym:

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

Powodem, dla którego chcesz to zrobić, jest to, że pozwala on kompilatorowi na obliczenie stałych wartości z wyprzedzeniem przed czasem wykonywania, co jest dobre, jeśli masz ich dużo.

Możesz także użyć prostej klasy do przekazywania wartości:

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);
}

Wszystkie świetne odpowiedzi, ale rozwiązanie klasy działa najlepiej w mojej sytuacji.
— EternusVia

8
Jeśli zmienimy te zmienne na globalne constexpr, przynajmniej otoczmy je namespacetak, aby nie opierały się na innych symbolach globalnych. Używanie zmiennej globalnej o nazwie Gjest po prostu wzywaniem do kłopotów.
— Wolfgang Bangerth

1
Dlaczego prowadzisz, włączając strażników z _? Nigdy nie powinieneś pisać niczego, co zaczyna się od _, ryzykujesz kolizję z kompilatorami. Powinieneś robić coś takiego ifndef PROJECT_NAME_FILE_NAME_EXTENSION. Nie wiesz też, dlaczego wstawiłeś wielkie litery, ale nie włączaj makr wartowników. Zasadniczo chcesz wykorzystać wszystkie makra, zwłaszcza że nie są one sanitarne. W przypadku stałych wielkie litery nie mają w ogóle sensu . Gjest w porządku, ponieważ jego SI, ale mass_earth jest bardziej odpowiednie i powinien być zakwalifikowany z przestrzenią nazw oznaczającą globalną, tj constants::mass_earth.
— kiedy

12

Inną alternatywą, która może być zgodna z twoim tokiem myślenia, jest użycie przestrzeni nazw (lub zagnieżdżonych przestrzeni nazw) do prawidłowego grupowania stałych. Przykładem może być:

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

Korzystając z powyższej techniki, możesz zlokalizować stałe odniesienia do niektórych pożądanych plików i przestrzeni nazw, dzięki czemu są one bardziej kontrolowane niż zmienne globalne, a jednocześnie uzyskujesz podobne korzyści. Kiedy używasz stałych, jest to tak proste, jak:

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

Jeśli nie lubisz długich łańcuchów zagnieżdżonych przestrzeni nazw, zawsze możesz skrócić rzeczy, gdy to konieczne, używając aliasu przestrzeni nazw:

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

2
Jest to podejście, po którym następuje OpenFOAM , patrz przypadkowy przykład kodu źródłowego OpenFOAM . OpenFOAM to biblioteka kodów C ++ implementująca metodę objętości skończonej, która jest szeroko stosowana w dynamice płynów.
— Dohn Joe

1

Jednym ze sposobów jest użycie singletona.

Po uruchomieniu programu inicjujesz singletona i wypełniasz go stałymi danymi (prawdopodobnie z pliku właściwości, który masz do uruchomienia). Dostajesz to w każdej klasie, której potrzebujesz wartości i po prostu z niej korzystaj.


Ostrzeżenie: czasami miałem singletony serializujące dostęp w kodzie wielowątkowym. Dlatego warto to sprawdzić w ramach etapu profilowania.
— Richard

Na pewno nie umieściłbym ich w singletonie ... W praktyce te stałe zmienią się w przyszłości, gdy (nie jeśli) zastosujesz swój model w innej dziedzinie. Posiadanie singletonu bardzo utrudnia testowanie przy użyciu różnych parametrów.
— André

Wszystkie są stałe. Nie ma tu potrzeby singletonu. Lepszym zastosowaniem jest tutaj statyczna klasa akcesorium. Jeszcze lepsza byłaby klasa statyczna, w której wartości są pobierane z pliku konfiguracyjnego (więc jeśli użytkownik końcowy zauważy błąd lub chce większej precyzji, może dostosować plik konfiguracyjny bez nowej kompilacji).
— Scuba Steve

Singletony są rzadko, jeśli w ogóle, dobrym pomysłem. Wstrzykiwanie zależności jest znacznie czystszym i bardziej elastycznym rozwiązaniem. Jednak z tylko stałymi, powiedziałbym, żeby po prostu trzymać je gdzieś w nagłówku.
— mascoj
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.