C ++ Praktik terbaik untuk berurusan dengan banyak konstanta, variabel dalam kode ilmiah

17

Saya mengembangkan kode untuk mensimulasikan aliran fluida dengan zat biologis yang ada dalam aliran. Ini melibatkan persamaan standar Navier-Stokes yang digabungkan ke beberapa model biologis tambahan. Ada banyak parameter / konstanta.

Saya memiliki fungsi tertulis untuk menangani perhitungan utama, tetapi masalah yang saya miliki adalah banyaknya konstanta / parameter yang menjadi sandaran perhitungan ini. Tampaknya rumit untuk melewatkan 10-20 argumen ke suatu fungsi.

Salah satu alternatif adalah membuat semua konstanta variabel global, tapi saya tahu ini disukai di C ++.

Apa cara standar menangani banyak input ke suatu fungsi? Haruskah saya membuat struct dan meneruskannya?

Terima kasih

EternusVia
sumber
7
Jika memungkinkan, usahakan konstanta dievaluasi pada waktu kompilasi menggunakan constexpr. Saya mencoba untuk memasukkan sebagian besar dalam file header terpisah. Untuk variabel, saya telah menemukan bahwa kelas yang terpisah memiliki manfaat, tetapi dengan potensi bug lebih banyak karena Anda harus menginisialisasi kelas sebelum melewati fungsi.
Biswajit Banerjee
3
Ini sulit dijawab dengan baik tanpa semacam contoh kode. Haruskah saya membuat struct dan meneruskannya? Secara umum, ya, ini benar-benar cara yang biasa dilakukan. Kelompokkan parameter / konstanta berdasarkan artinya.
Kirill
1
"Salah satu alternatif adalah membuat semua konstanta variabel global, tapi saya tahu ini disukai di C ++" Apakah itu?
Lightness Races dengan Monica
1
Apakah mereka benar-benar konstanta? Bagaimana jika Anda ingin menerapkan model Anda di domain yang berbeda? Saya akan merekomendasikan menempatkan mereka di kelas kecil. Setidaknya itu memberi Anda sedikit fleksibilitas di masa depan
André
@ André Sebagian besar dari mereka dikendalikan oleh pengguna melalui file parameter, itulah sebabnya saya setuju bahwa solusi kelas yang terbaik.
EternusVia

Jawaban:

13

Jika Anda memiliki konstanta yang tidak akan berubah sebelum berjalan, deklarasikan dalam file header:

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

Alasan mengapa Anda ingin melakukan ini adalah karena ia memungkinkan kompiler untuk menghitung nilai-nilai konstan sebelum waktu berjalan, yang bagus jika Anda memiliki banyak dari mereka.

Anda juga dapat menggunakan kelas sederhana untuk memberikan nilai:

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);
}
Richard
sumber
Semua jawaban bagus tetapi solusi kelas paling sesuai untuk situasi saya.
EternusVia
8
Jika Anda membuat variabel yang global constexpr, setidaknya lampirkan ke dalam namespacesehingga mereka tidak menginjak simbol global lainnya. Menggunakan variabel global yang dipanggil Ghanya memanggil masalah.
Wolfgang Bangerth
1
Mengapa Anda memimpin termasuk penjaga dengan _? Anda tidak boleh menulis apa pun yang dimulai dengan _, Anda berisiko tabrakan dengan kompiler vars. Anda seharusnya melakukan sesuatu seperti ifndef PROJECT_NAME_FILE_NAME_EXTENSION. Juga, tidak yakin mengapa Anda menggunakan huruf besar konstanta, tetapi tidak termasuk makro penjaga Anda. Anda umumnya ingin memanfaatkan semua makro, terutama karena mereka tidak sanitasi. Untuk kapitalisasi konstanta tidak masuk akal secara umum . Gbaik-baik saja karena SI-nya, tetapi mass_earth lebih tepat, dan harus memenuhi syarat dengan namespace untuk menandakan global yaitu constants::mass_earth.
whn
12

Alternatif lain yang mungkin sejalan dengan alur pemikiran Anda adalah dengan menggunakan namespace (atau ruang nama bersarang) untuk mengelompokkan konstanta dengan benar. Contohnya mungkin:

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

Dengan menggunakan teknik di atas, Anda dapat melokalisasi konstanta referensi ke beberapa file dan ruang nama yang diinginkan, menjadikannya lebih terkontrol daripada variabel global sambil mendapatkan beberapa manfaat serupa. Ketika Anda menggunakan konstanta, itu sesederhana melakukan:

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

Jika Anda tidak menyukai rantai panjang ruang nama bersarang, Anda selalu dapat mempersingkat hal-hal bila perlu dengan menggunakan alias namespace:

namespace const_earth = constants::earth;
constexpr double G_times_2 = 2.0*const_earth::G;
Spektr
sumber
2
Ini adalah pendekatan, yang diikuti oleh OpenFOAM , lihat contoh acak kode sumber OpenFOAM . OpenFOAM adalah pustaka kode C ++ yang mengimplementasikan metode volume hingga, yang banyak digunakan dalam dinamika fluida.
Dohn Joe
1

Salah satu cara yang saya lakukan adalah menggunakan singleton.

Ketika Anda memulai program Anda, Anda memulai singleton Anda dan mengisinya dengan data konstan (mungkin dari file properti yang Anda miliki untuk dijalankan). Anda mendapatkan ini di setiap kelas yang Anda butuhkan nilainya dan hanya menggunakannya.

Ashkan
sumber
Peringatan: Saya sesekali memiliki lajang serialisasi akses dalam kode multi-threaded. Jadi, Anda mungkin ingin memeriksa ini sebagai bagian dari tahap pembuatan profil Anda.
Richard
Saya pasti tidak akan menempatkan mereka dalam singleton ... Dalam prakteknya konstanta-konstanta itu akan berubah di masa depan ketika (tidak jika) Anda menerapkan model Anda di domain yang berbeda. Memiliki mereka seorang singleton membuatnya sangat sulit untuk kemudian diuji dengan parameter yang berbeda.
André
Mereka semua adalah konstanta. Di sini tidak perlu untuk seorang singleton. Kelas accessor statis lebih baik digunakan di sini. Bahkan yang lebih baik adalah kelas statis di mana nilai-nilai ditarik dari file konfigurasi (jadi jika pengguna akhir Anda melihat ada kesalahan, atau ingin lebih presisi, mereka dapat menyesuaikan file konfigurasi tanpa mendapatkan build baru).
Scuba Steve
Lajang jarang, jika pernah, ide yang bagus. Ketergantungan injeksi adalah solusi yang jauh lebih bersih dan lebih fleksibel. Namun, dengan konstanta saja, saya katakan hanya menyimpannya konstanta di header di suatu tempat.
mascoj