I am attempting to translate my Python program to C++, but because I am new to C++ I am encountering some problems. The input file is first parsed (works, not shown) to create the INITIAL_VALUES
dict/map, which I then want to use to assign the Parameters
class/struct attributes using the DEST_DICT_PARAMS
dict/map.
I was able to achieve this in Python code with:
import dataclasses
INITIAL_VALUES = {
"BULK": {
"MAGMA": {
"M0": 1.0,
"T0": 1320.0,
},
"ASSIM": {
"M0": 0.0,
"T0": 600.0,
},
}
}
DEST_DICT_PARAMS = {
'M0': {"MAGMA": 'Mm0', "ASSIM": 'Ma0'},
'T0': {"MAGMA": 'Tm0', "ASSIM": 'Ta0'},
}
@dataclasses.dataclass
class Parameters:
Mm0: float = None
Ma0: float = None
Ta0: float = None
Tm0: float = None
class ParametersReader:
def __init__(self):
self.parameters = Parameters()
self._assignParameters()
def _assignParameters(self):
for param_fam, dest in DEST_DICT_PARAMS.items():
for component, param in dest.items():
value = INITIAL_VALUES["BULK"][component][param_fam]
setattr(self.parameters, param, value)
params = ParametersReader()
print(params.parameters)
Output:
Parameters(Mm0=1.0, Ma0=0.0, Ta0=600.0, Tm0=1320.0)
So I wrote the corresponding C++ code:
#include <iostream>
#include <map>
using std::map;
using std::string;
map<string, map<string, map<string, float> > > INITIAL_VALUES = {{
"BULK", {
{"MAGMA", {
{"M0", 1.0},
{"T0", 1320.0},
}},
{"ASSIM", {
{"M0", 0.0},
{"T0", 600.0},
}},
}
}};
map<string, map<string, string> > DEST_DICT_PARAMS = {{
{"M0", {{"MAGMA", "Mm0"}, {"ASSIM", "Ma0"}}},
{"T0", {{"MAGMA", "Tm0"}, {"ASSIM", "Ta0"}}},
}};
struct Parameters {
float Mm0;
float Ma0;
float Ta0;
float Tm0;
} parameters;
class ParametersReader {
public:
void assignParameters_() {
map<string, map<string, string> >::iterator itr0;
map<string, string>::iterator itr1;
for (itr0 = DEST_DICT_PARAMS.begin(); itr0 != DEST_DICT_PARAMS.end(); itr0++) {
for (itr1 = itr0->second.begin(); itr1 != itr0->second.end(); itr1++) {
parameters.itr1->second = INITIAL_VALUES["BULK"][itr1->first];
}
}
}
};
int main() {
ParametersReader params;
params.assignParameters_();
}
But I'm getting an error at the line
parameters.itr1->second = INITIAL_VALUES['BULK'][itr1->first]
saying "no member named 'itr1' in 'Parameters'". That error makes total sense because the code is literally trying to interpret 'itr1' as an attribute name and not the whole 'itr1->second' as the name. I think this comes down to the fact that I can't seem to find a C++ equivalent to Python's setattr(obj, name, val)
function that takes an object and its attribute name and assigns it a value. Is there a C++ solution to what I am attempting?
Perhaps my entire approach is incompatible with C++. If so, would you kindly suggest an alternative approach? I would like to keep the input file format the same between the Python and C++ versions.
C++ does not have runtime reflection like Python. You cannot look up a class member by name using a runtime string because class member names do not exist at runtime.
What you can do is look up a class member via a pointer to member. This is an offset into the object calculated at compile time by the compiler:
std::map<std::string, std::map<std::string, float Parameters::*> > DEST_DICT_PARAMS = {{
{"M0", {{"MAGMA", &Parameters::Mm0}, {"ASSIM", &Parameters::Ma0}}},
{"T0", {{"MAGMA", &Parameters::Tm0}, {"ASSIM", &Parameters::Ta0}}},
}};
class ParametersReader {
public:
void assignParameters_() {
for (auto& [param_fam, dest] : DEST_DICT_PARAMS) {
for (auto& [component, param] : dest) {
parameters.*param = INITIAL_VALUES["BULK"][component][param_fam];
}
}
}
};
Note I've also used range-based for loops and structured bindings to clean up your assignParameters_
function.