如何按名称公开类的字段
How to expose fields of a class by name
我想定义结构来保存各种应用程序参数:
struct Params
{
String fooName;
int barCount;
bool widgetFlags;
// ... many more
};
但我希望能够按名称枚举、获取和设置这些字段,例如,我可以将它们公开给自动化 API 并简化序列化:
Params p;
cout << p.getField("barCount");
p.setField("fooName", "Roger");
for (String fieldname : p.getFieldNames()) {
cout << fieldname << "=" << p.getField(fieldName);
}
有没有定义从字符串标签到 get/set 函数的绑定的好方法? 按照这个(非常伪代码)的思路:
Params() {
addBinding("barCount", setter(&Params::barCount), getter(&Params::barCount));
...
我知道其他选项是从外部元数据文件自动生成结构,另一种选择是从(键,值)对的表存储结构,但我宁愿将数据保存在结构中。
我确实有一个所有字段都可以转换为的Variant
类型。
反射,所以这不是你可以干净利落地做的事情。此外,通过将成员引用为字符串,您必须尝试回避语言的强类型性质。使用像Boost Serializer或Google Protobuf这样的序列化库可能更有用。
也就是说,如果我们允许一些可怕的事情,人们可以用XMacro做点什么。(免责声明:我不建议实际这样做)。首先,您将所需的所有信息放入宏中
#define FIELD_PARAMS
FIELD_INFO(std::string, Name, "Name")
FIELD_INFO(int, Count, "Count")
或者放入头文件中
<defs.h>
FIELD_INFO(std::string, Name, "Name")
FIELD_INFO(int, Count, "Count")
然后,您将在类中定义FIELD_INFO表示成员声明,或将它们添加到映射中
struct Params{
Params() {
#define FIELD_INFO(TYPE,NAME,STRNAME) names_to_members.insert(std::make_pair(STRNAME,&NAME));
FIELD_PARAMS
#undef FIELD_INFO
}
template <typename T>
T& get(std::string field){
return *(T*)names_to_members[field];
}
std::map<std::string, void*> names_to_members;
#define FIELD_INFO(TYPE,NAME,STRNAME) TYPE NAME;
FIELD_PARAMS
#undef FIELD_INFO
};
然后你可以像这样使用它
int main (int argc, char** argv){
Params myParams;
myParams.get<std::string>("Name") = "Mike";
myParams.get<int>("Count") = 38;
std::cout << myParams.get<std::string>("Name"); // or myParams.Name
std::cout << std::endl;
std::cout << myParams.get<int>("Count"); // or myParams.Count
return 0;
}
不幸的是,您仍然需要告诉编译器类型是什么。如果你有一个很好的变体类和可以很好地使用它的库,你也许能够解决这个问题。
我为此使用了略有不同的存储:这里。出于某种原因,我使用的标签是整数,但您也可以使用 std::string 键。
没有真正好的方法(无论如何,"好"是一个非常主观的方面),因为无论您选择哪种技术都不是C++语言本身的一部分,但是如果您的目标是序列化,请查看 Boost 序列化。
我设法想出了一些满足我特定需求的东西。Ari的答案在将字符串映射到对成员变量的引用方面最接近,尽管它依赖于void*
的强制转换。 我有一些更类型安全的东西:
单个PropertyAccessor
有一个接口,该接口具有从中派生的模板化类,该类绑定到对特定成员变量的引用,并在 Variant 表示形式之间相互转换:
class IPropertyAccessor
{
public:
virtual ~IPropertyAccessor() {}
virtual Variant getValueAsVariant() const =0;
virtual void setValueAsVariant(const Variant& variant) =0;
};
typedef std::shared_ptr<IPropertyAccessor> IPropertyAccessorPtr;
template <class T>
class PropertyAccessor : public IPropertyAccessor
{
public:
PropertyAccessor(T& valueRef_) : valueRef(valueRef_) {}
virtual Variant getValueAsVariant() const {return VariantConverter<T>().toVariant(valueRef); }
virtual void setValueAsVariant(const Variant& variant) {return VariantConverter<T>().toValue(variant); }
T& valueRef;
};
// Helper class to create a propertyaccessor templated on a type
template <class T>
static IPropertyAccessorPtr createAccessor(T& valueRef_)
{
return std::make_shared<PropertyAccessor<T>>(valueRef_);
}
公开集合的类现在可以定义 ID -> PropertyAccessor 并通过引用绑定其值:
#define REGISTER_PROPERTY(field) accessorMap.insert(AccessorMap::value_type(#field, createAccessor(field)))
class TestPropertyCollection
{
public:
typedef std::map<PropertyID, IPropertyAccessorPtr> AccessorMap;
TestPropertyCollection()
{
REGISTER_PROPERTY(stringField1);
// expands to
// accessorMap.insert(AccessorMap::value_type("stringField", createAccessor(stringField)));
REGISTER_PROPERTY(stringField2);
REGISTER_PROPERTY(intField1);
}
bool getPropertyVariant(const PropertyID& propertyID, Variant& retVal)
{
auto it = accessorMap.find(propertyID);
if (it != accessorMap.end()) {
auto& accessor = it->second;
retVal = accessor->getValueAsVariant();
return true;
}
return false;
}
String stringField1;
String stringField2;
int intField1;
AccessorMap accessorMap
};
- 将结构字段的类型展开为可变模板参数
- 将位字段导出到数组
- 为了方便起见,我应该避免公开私有字段变量吗
- 当字段可以为null时,如何使用C++接口在Avro中写入数据
- 在java中读取c++字节的位字段
- 我正在尝试一个傻瓜 C++ 练习,我遇到了一个错误,说类 'GraduateStudent' 没有任何名为 'advisor' 的字段
- 自动复制=按值并支持多态性的适当类型的非静态字段
- 按字段过滤消息
- 使 TAB 按转到下一个字段
- 按一个字段对自定义对象向量进行排序
- DBGrid按计算字段排序
- 如何按字段名称为 QTableWidget 编制索引
- C++按字段读取结构,并将结构直接写入流
- 使用Protocol Buffers检索带有字段描述符的父消息中的字段名
- Lambda 捕获列表:如果不捕获整个对象,就不可能按值捕获对象的成员字段?
- 按索引或字段分配在C++中不起作用
- 消息扩展名不能有必需的字段
- 从任何结构或元组按字符串形式的字段键获取迭代器
- 如何按名称公开类的字段
- 如何处理与字段名同名的参数