我想定义结构来保存各种应用程序参数:
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
类型,所有字段都可以转换为。
答案 0 :(得分:1)
C ++没有反思,所以这不是你可以干净利落的事情。此外,通过将成员称为字符串,您必须尝试支持该语言的强类型性质。使用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;
}
不幸的是,您仍然需要告诉编译器类型是什么。如果你有一个很好的变体类和适合它的库,你可以解决这个问题。
答案 1 :(得分:0)
我使用略有不同的存储空间:here。我使用的标签由于某种原因是int,但你也可以使用std :: string键。
答案 2 :(得分:0)
没有非常好的方法(无论如何“好”是一个非常主观的方面),因为你选择的任何技术都不是C ++语言本身的一部分,但如果你的目标是序列化,那么看一下{{3 }}
答案 3 :(得分:0)
我设法满足了我的特殊需求。 Ari的答案在将字符串映射到成员变量的引用方面最接近,尽管它依赖于void*
的强制转换。我有一些类型更安全的东西:
有一个单独的PropertyAccessor
的接口,它有一个从它派生的模板化类,它绑定到对特定成员变量的引用,并转换为Variant表示和从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 - &gt; 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
};