Skip to content

Commit 762c8ce

Browse files
committed
Refactor code to use getter and setter methods for configuration classes
- Updated `.clang-tidy` to ignore 'google-readability-todo' check. - Introduced a new `class_to_yaml_converter.md` with example usage of `yaml-cpp` to convert class objects. - Added a new `struct_to_class.md` with examples of converting structs to classes with getters and setters. - Refactored `ModelSettings`, `PopulationDemographic`, and `TransmissionSettings` classes to use getters and setters. - Updated YAMLConverters to use the new getters and setters for `ModelSettings`, `PopulationDemographic`, and `TransmissionSettings`. - Modified `Config` class and removed observer pattern in favor of cleaner cross-field validation. - Updated related tests to use getter and setter methods instead of directly accessing member variables.
1 parent 64906d3 commit 762c8ce

14 files changed

+593
-217
lines changed

.clang-tidy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Checks: >
1313
-misc-non-private-member-variables-in-classes,
1414
-readability-braces-around-statements,
1515
-google-readability-braces-around-statements,
16+
-google-readability-todo,
1617
-cppcoreguidelines-avoid-magic-numbers,
1718
-readability-magic-numbers,
1819
-readability-magic-numbers,

promts/class_to_yaml_converter.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
```
2+
Here is the example of using yaml-cpp to read in yaml node to class object
3+
4+
class ModelSettings {
5+
public:
6+
// Getters
7+
[[nodiscard]] int get_days_between_stdout_output() const {
8+
return days_between_stdout_output_;
9+
}
10+
// Setters with validation
11+
void set_days_between_stdout_output(int value) {
12+
if (value <= 0)
13+
throw std::invalid_argument(
14+
"days_between_stdout_output must be greater than 0");
15+
days_between_stdout_output_ = value;
16+
}
17+
[[nodiscard]] int get_initial_seed_number() const {
18+
return initial_seed_number_;
19+
}
20+
void set_initial_seed_number(int value) {
21+
if (value < 0)
22+
throw std::invalid_argument("initial_seed_number must be non-negative");
23+
initial_seed_number_ = value;
24+
}
25+
[[nodiscard]] bool get_record_genome_db() const { return record_genome_db_; }
26+
void set_record_genome_db(bool value) { record_genome_db_ = value; }
27+
[[nodiscard]] const date::year_month_day &get_starting_date() const {
28+
return starting_date_;
29+
}
30+
void set_starting_date(const date::year_month_day &value) {
31+
starting_date_ = value;
32+
}
33+
[[nodiscard]] const date::year_month_day &get_start_of_comparison_period()
34+
const {
35+
return start_of_comparison_period_;
36+
}
37+
void set_start_of_comparison_period(const date::year_month_day &value) {
38+
if (value < starting_date_)
39+
throw std::invalid_argument(
40+
"start_of_comparison_period cannot be before starting_date");
41+
start_of_comparison_period_ = value;
42+
}
43+
[[nodiscard]] const date::year_month_day &get_ending_date() const {
44+
return ending_date_;
45+
}
46+
void set_ending_date(const date::year_month_day &value) {
47+
if (value < start_of_comparison_period_)
48+
throw std::invalid_argument(
49+
"ending_date cannot be before start_of_comparison_period");
50+
ending_date_ = value;
51+
}
52+
[[nodiscard]] int get_start_collect_data_day() const {
53+
return start_collect_data_day_;
54+
}
55+
void set_start_collect_data_day(int value) {
56+
if (value < 0)
57+
throw std::invalid_argument(
58+
"start_collect_data_day must be non-negative");
59+
start_collect_data_day_ = value;
60+
}
61+
62+
private:
63+
int days_between_stdout_output_;
64+
int initial_seed_number_;
65+
bool record_genome_db_;
66+
date::year_month_day starting_date_;
67+
date::year_month_day start_of_comparison_period_;
68+
date::year_month_day ending_date_;
69+
int start_collect_data_day_;
70+
};
71+
72+
73+
template <>
74+
struct convert<ModelSettings> {
75+
static Node encode(const ModelSettings &rhs) {
76+
Node node;
77+
node["days_between_stdout_output"] = rhs.get_days_between_stdout_output();
78+
node["initial_seed_number"] = rhs.get_initial_seed_number();
79+
node["record_genome_db"] = rhs.get_record_genome_db();
80+
node["starting_date"] = rhs.get_starting_date();
81+
node["start_of_comparison_period"] = rhs.get_start_of_comparison_period();
82+
node["ending_date"] = rhs.get_ending_date();
83+
node["start_collect_data_day"] = rhs.get_start_collect_data_day();
84+
return node;
85+
}
86+
87+
static bool decode(const Node &node, ModelSettings &rhs) {
88+
if (!node["days_between_stdout_output"]) {
89+
throw std::runtime_error("Missing 'days_between_stdout_output' field.");
90+
}
91+
if (!node["initial_seed_number"]) {
92+
throw std::runtime_error("Missing 'initial_seed_number' field.");
93+
}
94+
if (!node["record_genome_db"]) {
95+
throw std::runtime_error("Missing 'record_genome_db' field.");
96+
}
97+
if (!node["starting_date"]) {
98+
throw std::runtime_error("Missing 'starting_date' field.");
99+
}
100+
if (!node["start_of_comparison_period"]) {
101+
throw std::runtime_error("Missing 'start_of_comparison_period' field.");
102+
}
103+
if (!node["ending_date"]) {
104+
throw std::runtime_error("Missing 'ending_date' field.");
105+
}
106+
if (!node["start_collect_data_day"]) {
107+
throw std::runtime_error("Missing 'start_collect_data_day' field.");
108+
}
109+
110+
// TODO: Add more error checking for each field
111+
112+
rhs.set_days_between_stdout_output(
113+
node["days_between_stdout_output"].as<int>());
114+
rhs.set_initial_seed_number(node["initial_seed_number"].as<int>());
115+
rhs.set_record_genome_db(node["record_genome_db"].as<bool>());
116+
rhs.set_starting_date(node["starting_date"].as<date::year_month_day>());
117+
rhs.set_start_of_comparison_period(
118+
node["start_of_comparison_period"].as<date::year_month_day>());
119+
rhs.set_ending_date(node["ending_date"].as<date::year_month_day>());
120+
rhs.set_start_collect_data_day(node["start_collect_data_day"].as<int>());
121+
return true;
122+
}
123+
};
124+
125+
As an expert in C++ developer, would you mind make the similar convert function for the following class:
126+
```

promts/struct_to_class.md

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
```
2+
Here is an example of conversion a struct into class:
3+
4+
5+
struct PopulationDemographic {
6+
int number_of_age_classes;
7+
std::vector<int> age_structure;
8+
std::vector<int> initial_age_structure;
9+
double birth_rate;
10+
std::vector<double> death_rate_by_age_class;
11+
std::vector<double> mortality_when_treatment_fail_by_age_class;
12+
double artificial_rescaling_of_population_size;
13+
};
14+
15+
class PopulationDemographic {
16+
public:
17+
// Getters
18+
[[nodiscard]] int get_number_of_age_classes() const {
19+
return number_of_age_classes_;
20+
}
21+
// Setters with validation
22+
void set_number_of_age_classes(int value) {
23+
if (value <= 0)
24+
throw std::invalid_argument(
25+
"number_of_age_classes must be greater than 0");
26+
number_of_age_classes_ = value;
27+
}
28+
29+
[[nodiscard]] const std::vector<int> &get_age_structure() const {
30+
return age_structure_;
31+
}
32+
void set_age_structure(const std::vector<int> &value) {
33+
if (value.size() != number_of_age_classes_)
34+
throw std::invalid_argument(
35+
"age_structure size must match number_of_age_classes");
36+
age_structure_ = value;
37+
}
38+
39+
[[nodiscard]] const std::vector<int> &get_initial_age_structure() const {
40+
return initial_age_structure_;
41+
}
42+
void set_initial_age_structure(const std::vector<int> &value) {
43+
if (value.size() != number_of_age_classes_)
44+
throw std::invalid_argument(
45+
"initial_age_structure size must match number_of_age_classes");
46+
initial_age_structure_ = value;
47+
}
48+
49+
[[nodiscard]] double get_birth_rate() const { return birth_rate_; }
50+
void set_birth_rate(double value) {
51+
if (value < 0)
52+
throw std::invalid_argument("birth_rate must be non-negative");
53+
birth_rate_ = value;
54+
}
55+
56+
[[nodiscard]] const std::vector<double> &get_death_rate_by_age_class() const {
57+
return death_rate_by_age_class_;
58+
}
59+
60+
void set_death_rate_by_age_class(const std::vector<double> &value) {
61+
if (value.size() != number_of_age_classes_)
62+
throw std::invalid_argument(
63+
"death_rate_by_age_class size must match number_of_age_classes");
64+
death_rate_by_age_class_ = value;
65+
}
66+
67+
[[nodiscard]] const std::vector<double> &
68+
get_mortality_when_treatment_fail_by_age_class() const {
69+
return mortality_when_treatment_fail_by_age_class_;
70+
}
71+
72+
void set_mortality_when_treatment_fail_by_age_class(
73+
const std::vector<double> &value) {
74+
if (value.size() != number_of_age_classes_)
75+
throw std::invalid_argument(
76+
"mortality_when_treatment_fail_by_age_class size must match "
77+
"number_of_age_classes");
78+
mortality_when_treatment_fail_by_age_class_ = value;
79+
}
80+
81+
[[nodiscard]] double get_artificial_rescaling_of_population_size() const {
82+
return artificial_rescaling_of_population_size_;
83+
}
84+
85+
void set_artificial_rescaling_of_population_size(double value) {
86+
if (value <= 0)
87+
throw std::invalid_argument(
88+
"artificial_rescaling_of_population_size must be greater than 0");
89+
artificial_rescaling_of_population_size_ = value;
90+
}
91+
92+
private:
93+
int number_of_age_classes_;
94+
std::vector<int> age_structure_;
95+
std::vector<int> initial_age_structure_;
96+
double birth_rate_;
97+
std::vector<double> death_rate_by_age_class_;
98+
std::vector<double> mortality_when_treatment_fail_by_age_class_;
99+
double artificial_rescaling_of_population_size_;
100+
};
101+
102+
103+
104+
As an expert C++ developer, would you mind help me convert the following struct.
105+
Requirments:
106+
The getter and setter should be in pair, next to each other.
107+
108+
109+
```

promts/struct_to_yaml_converter.md

Lines changed: 0 additions & 46 deletions
This file was deleted.

src/Configuration/Config.cpp

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,20 @@
88
#include "YAMLConverters.h"
99

1010
void Config::load(const std::string &filename) {
11-
std::shared_lock lock(mutex_);
1211
config_file_path_ = filename;
1312
YAML::Node config = YAML::LoadFile(filename);
14-
config_data_.model_settings = config["ModelSettings"].as<ModelSettings>();
13+
14+
config_data_.model_settings = config["model_settings"].as<ModelSettings>();
15+
1516
config_data_.transmission_settings =
16-
config["TransmissionSettings"].as<TransmissionSettings>();
17-
config_data_.population_demographic =
18-
config["PopulationDemographic"].as<PopulationDemographic>();
19-
notify_observers();
20-
}
17+
config["transmission_settings"].as<TransmissionSettings>();
2118

22-
void Config::reload() { load(config_file_path_); }
19+
config_data_.population_demographic =
20+
config["population_demographic"].as<PopulationDemographic>();
2321

24-
void Config::register_observer(ConfigObserver observer) {
25-
std::unique_lock lock(mutex_);
26-
observers_.push_back(observer);
22+
// Validate all cross field validations
23+
validate_all_cross_field_validations();
2724
}
2825

29-
void Config::notify_observers() {
30-
for (const auto &observer : observers_) { observer(config_data_); }
31-
}
26+
void Config::reload() { load(config_file_path_); }
3227

0 commit comments

Comments
 (0)