c++ 如何通过编程方式分配类/结构体属性?

mwecs4sa  于 2023-02-17  发布在  其他
关注(0)|答案(2)|浏览(164)

我试图将Python程序翻译成C++,但是因为我是C++新手,所以遇到了一些问题:首先解析输入文件(可以工作,未显示)以创建INITIAL_VALUES dict/map,然后我想使用DEST_DICT_PARAMS dict/map来分配Parameters class/struct属性。
我可以用Python代码实现这一点:

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)

输出:
参数(平均温度= 1.0,平均湿度= 0.0,平均温差= 600.0,平均温差= 1320.0)
所以我写了相应的C++代码:

#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_();
}

但是我在parameters.itr1->second = INITIAL_VALUES['BULK'][itr1->first]行遇到一个错误,说"'Parameters'中没有名为'itr1'的成员"。这个错误完全有意义,因为代码试图将'itr1'解释为属性名称,而不是将整个'itr1-〉second'解释为名称。我认为这归结为一个事实,即我似乎找不到Python的C等价物。s setattr(obj, name, val)函数,该函数接受一个对象及其属性名并为其赋值。对于我正在尝试的操作,有C解决方案吗?
也许我的整个方法都与C不兼容。如果是这样,你能建议一种替代方法吗?我希望Python和C版本的输入文件格式保持一致。

ryevplcw

ryevplcw1#

C++不像Python那样有运行时反射。你不能使用运行时字符串按名称查找类成员,因为类成员名称在运行时不存在。
你 * 能 * 做的就是通过一个 * 指向member* 的指针 * 来查找类成员,这是编译器在编译时计算的对象偏移量:

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

Demo
注我还使用了基于范围的for循环和结构化绑定来清理assignParameters_函数。

1hdlvixo

1hdlvixo2#

C++没有Python setattr(self.parameters, param, value)的等价物。如果你想在字符串和成员之间有一个Map,你需要自己编写它。
您可以使用指向成员的指针执行以下操作:

#include <map>
#include <iostream>

struct foo {
    float a = 0.0f;
    float b = 0.0f;
};

// list all members and their string represenation
std::map<std::string,float foo::*> mmap {
    { "a", &foo::a },
    { "b", &foo::b }
};

int main() {
    foo f;
    std::map<std::string,float> values {
        {"a", 0.1},
        {"b", 0.2}
    };

    
    for (const auto& val : values) {
        // lookup the member pointers via the strings from mmap
        // use the found function pointer to assing to corresponding member in f
        f.*mmap[val.first] = val.second;
    }

    std::cout << f.a << " " << f.b << "\n";
}

请注意,代码假定values中存在的所有字符串也存在于mmap中。如果不存在,它将严重失败。要修复此问题,应改用mmap.find,并适当处理未找到字符串的情况。
这是可行的,尽管没有办法只从类定义中隐式地得到Map,但另一方面,我可以想象库的存在可以帮助实现这一点。

相关问题