C ++ แนวทางปฏิบัติที่ดีที่สุดสำหรับการจัดการกับค่าคงที่จำนวนมากตัวแปรในรหัสทางวิทยาศาสตร์


17

ฉันกำลังพัฒนารหัสเพื่อจำลองการไหลของของเหลวกับสารชีวภาพที่มีอยู่ในการไหล เรื่องนี้เกี่ยวข้องกับสมการเนเวียร์ - สโตกส์คู่กับแบบจำลองทางชีวภาพเพิ่มเติม มีหลายพารามิเตอร์ / ค่าคงที่

ฉันได้เขียนฟังก์ชันเพื่อจัดการการคำนวณที่สำคัญ แต่ปัญหาที่ฉันมีคือค่าคงที่ / พารามิเตอร์จำนวนมากที่การคำนวณเหล่านี้ขึ้นอยู่กับ ดูเหมือนว่ายุ่งยากที่จะส่งผ่านอาร์กิวเมนต์ 10-20 ข้อไปยังฟังก์ชัน

อีกทางเลือกหนึ่งคือการทำให้ค่าคงที่ทั่วโลกตัวแปรทั้งหมด แต่ฉันรู้ว่านี่คือขมวดคิ้วใน C ++

วิธีมาตรฐานในการจัดการอินพุตจำนวนมากให้กับฟังก์ชันคืออะไร ฉันควรสร้าง struct และส่งมันแทนหรือไม่?

ขอขอบคุณ


7
หากเป็นไปได้ให้ลองประเมินค่าคงที่ ณ เวลารวบรวมโดยใช้ constexpr ฉันพยายามรวมส่วนใหญ่เหล่านี้ในไฟล์ส่วนหัวแยก สำหรับตัวแปรฉันพบว่าคลาสที่แยกต่างหากมีประโยชน์ แต่ด้วยค่าใช้จ่ายของข้อบกพร่องที่อาจเกิดขึ้นได้มากขึ้นเพราะคุณต้องเริ่มต้นคลาสก่อนที่จะผ่านไปยังฟังก์ชัน
Biswajit Banerjee

3
นี่เป็นการยากที่จะตอบอย่างถูกต้องโดยไม่มีตัวอย่างโค้ด ฉันควรสร้าง struct และส่งมันแทนหรือไม่? โดยทั่วไปแล้วใช่นี่เป็นวิธีปกติอย่างแน่นอน จัดกลุ่มพารามิเตอร์ / ค่าคงที่ตามความหมาย
คิริลล์

1
"ทางเลือกหนึ่งคือการสร้างค่าคงที่ตัวแปรโกลบอลทั้งหมด แต่ฉันรู้ว่านี่คือ frowned ใน C ++"ใช่หรือไม่
การแข่งขัน Lightness กับโมนิก้า

1
พวกเขายังคงอยู่จริงๆเหรอ? ถ้าคุณต้องการใช้โมเดลของคุณในโดเมนอื่น ฉันอยากจะแนะนำให้พวกเขาอยู่ในชั้นเรียนเล็ก ๆ อย่างน้อยก็ช่วยให้คุณมีความยืดหยุ่นเล็กน้อยในอนาคต
André

@ 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เป็นเพียงการโทรหาปัญหา
Wolfgang Bangerth

1
ทำไมคุณถึงนำผู้คุมรวมด้วย _ คุณไม่ควรเขียนอะไรที่ขึ้นต้นด้วย _ คุณเสี่ยงต่อการชนกับคอมไพเลอร์ vars ifndef PROJECT_NAME_FILE_NAME_EXTENSIONคุณควรจะทำสิ่งที่ชอบ นอกจากนี้ไม่แน่ใจว่าทำไมคุณจึงต้องพิมพ์ใหญ่ให้คงที่ แต่ไม่รวมมาโครป้องกันของคุณ โดยทั่วไปคุณต้องการใช้ประโยชน์จากแมโครทั้งหมดโดยเฉพาะอย่างยิ่งเพราะไม่สะอาด สำหรับโครงสร้างเงินทุนคงไม่ได้ทำให้รู้สึกโดยทั่วไป Gเป็นเรื่องปกติเพราะ SI แต่ mass_earth มีความเหมาะสมมากขึ้นและควรจะมีคุณสมบัติที่มี namespace 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 เป็นห้องสมุดรหัส C ++ ที่ใช้วิธีไฟไนต์วอลุ่มซึ่งใช้กันอย่างแพร่หลายในพลศาสตร์ของไหล
Dohn Joe

1

วิธีหนึ่งที่ฉันทำคือใช้ซิงเกิล

เมื่อคุณเริ่มโปรแกรมของคุณคุณจะเริ่มต้นซิงเกิลตันและเติมข้อมูลคงที่ (อาจมาจากไฟล์คุณสมบัติที่คุณมีสำหรับการรัน) คุณได้สิ่งนี้ในทุก ๆ คลาสที่คุณต้องการค่าและใช้มัน


คำเตือน: บางครั้งฉันมีการเข้าถึงซีเรียลไลซ์ตันแบบอนุกรมในรหัสแบบมัลติเธรด ดังนั้นคุณอาจต้องการตรวจสอบเรื่องนี้เป็นส่วนหนึ่งของขั้นตอนการทำโปรไฟล์ของคุณ
ริชาร์ด

แน่นอนว่าฉันจะไม่ใส่พวกเขาไว้ในซิงเกิล ... ในทางปฏิบัติค่าคงที่เหล่านั้นจะเปลี่ยนไปในอนาคตเมื่อ (ไม่ใช่ถ้า) คุณใช้โมเดลของคุณในโดเมนอื่น การที่พวกเขามีซิงเกิลตันนั้นยากมากที่จะทดสอบด้วยพารามิเตอร์ที่แตกต่างกัน
André

พวกมันคงที่ทั้งหมด ไม่จำเป็นต้องอยู่ที่นี่สักพัก คลาส accessor แบบคงที่จะใช้งานได้ดีกว่าที่นี่ ยิ่งไปกว่านั้นจะเป็นคลาสสแตติกที่ดึงค่าจากไฟล์ปรับแต่ง (ดังนั้นหากผู้ใช้ของคุณเห็นว่ามีข้อผิดพลาดหรือต้องการความแม่นยำมากขึ้นพวกเขาสามารถปรับไฟล์ปรับแต่งได้โดยไม่ต้องสร้างใหม่)
Scuba Steve

ความคิดที่ดี การฉีดพึ่งพาเป็นวิธีที่สะอาดและยืดหยุ่นมากขึ้น อย่างไรก็ตามด้วยค่าคงที่ฉันจะบอกว่าให้ค่าคงที่อยู่ในส่วนหัวบางแห่ง
mascoj
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.