2016-09-17 45 views
2

我有一个结构,像这样:有没有办法将字符串转换为C++/Qt中的结构中的字段?

typedef struct { 
    int field1; 
    int field2; 
} Example; 

Example data; 

我真正的结构具有约20场。不过,我想根据解析文本文件为这些字段赋值。现在,这样做的一个显而易见的方法是用一个巨大的开关(或链接)测试分配的属性。然而,这很麻烦并且很难扩展。

是否有任何机制可以将字符串从文件转换为struct字段名称?因此,例如,如果我的文件有一条线field1 = 23,我会知道将23分配给data.field1而没有switch

+0

没有有没有。运行时不知道你的变量名,所以它不能知道它本身。 –

+0

有没有办法做到这一点,但可能是你可以实现某种责任_模式_chain有一个简单的解决方案,而不是一个长的if-else链。 –

+0

对不起,我不知道什么是责任模式链。你能详细说明一下吗? – aarelovich

回答

0

你需要编写制定者:

void setField1(const char *f) ; 
void setField2(const char *f) ; 

的制定者有责任将字符串解码成一个int。

然后,你需要的字段名关联到功能的关联数组(哈希):

"field1" -> setField1 
"field2" -> setField2 

然后,你需要从关联数组抢函数指针,并与现场的参数调用它。这是一个例子:

#include <iostream> 

typedef struct { 
    int field1; 
    int field2; 
} Example; 

Example data; 

void setField1(const char *f) { 
    data.field1 = std::stoi(f); 
} 

void setField2(const char *f) { 
    data.field2 = std::stoi(f); 
} 

void (*tab[])(const char *) = { setField1, setField2 }; 

int main(void) 
{ 
    data.field1 = 10 ; 
    data.field2 = 20 ; 
    tab[0]("43") ; 
    tab[1]("12") ; 
    std::cout << "fields:" << data.field1 << '|' << data.field2 << std::endl; 
    return 0; 
} 

当然,你需要做的不止是做出正确的代码。例如,示例应该是一个类,并且setters移动到那里,然后您需要解码文本行以获取索引或使用关联数组。

2

一种选择是使用Qt的元对象系统功能按名称调用方法。

class SetFieldHelper : public QObject 
{ 
    Q_OBJECT 
public: 
    explicit SetFieldHelper(Example &data) : m_data(data) {} 

    void setField(const QString &line) { 
     QStringList parts = line.split('='); 
     if (parts.count() == 2) { 
      const QByteArray field = parts[0].trimmed().toUtf8(); 
      const QString value = parts[1].trimmed(); 

      QMetaObject::invokeMethod(this, field.constData(), Q_ARG(QString, value)); 
     } 
    } 

private slots: 
    void field1(const QString &value) { 
     m_data.field1 = value.toInt(); 
    } 

private: 
    Example &m_data; 
}; 
+0

如果我正在认真对待你的例子,你需要为每个领域的领域功能,对吧?我认为这是最好的选择,只要我更容易使用Qt功能。 – aarelovich

+0

是的,您需要为每个字段指定一个专用插槽,每个插槽使用文件中使用的字段标识符命名。 –

+0

如果你使用Qt的元数据,你可以直接声明属性并直接访问它们。然后它甚至不需要成为'QObject'。 –

2

有一个很好的方法可以做到这一点,使用旧技术称为X Macro

下面是一个例子:

struct A 
{ 
    #define STRUCT_A_MEMBER_LIST \ 
     X(foo) \ 
     X(bar) 

    #define X(name) int name; 
    STRUCT_A_MEMBER_LIST 
    #undef X 

    void SetFieldByName(const char *field_name, int value) 
    { 
     #define X(name) if (!std::strcmp(field_name, #name)) name = std::atoi(value); else 
     STRUCT_A_MEMBER_LIST return; 
     #undef X 
    } 

    #undef STRUCT_A_MEMBER_LIST 
} 

有可能更换的if-else-IF链具有哈希映射或其他什么东西,但你不会需要手动指定字段名两种方式。

+0

这太好了。谢谢! – aarelovich

+0

我想你打算做'std :: strcmp(field_name,#name)'? – Mike

+0

@Mike谢谢,修复。 – HolyBlackCat

2

您可以通过Q_GADGET来利用Qt的metaobject系统。这具有以下优点:

  1. 无需添加getter/setter方法 - 他们可以(如果可用),但Qt可以直接访问原始或Qt的类型的成员。

  2. 无需处理将字符串转换为目标类型。元属性系统为您自动进行必要的转换。

  3. 元数据没有开销 - 它不会将任何数据添加到目标类型,因为目标类型保持不变。

    即使如此,Q_PROPERTY是一个空的宏,Q_GADGET增加了一个静态方法的类。

如果你问我,这是一个相当不错的杠杆:)

// https://github.com/KubaO/stackoverflown/tree/master/questions/struct-deserialize-39547524 
#include <QtCore> 
#include <string> 

// Original API 

struct Example1 { 
    int field1; 
    double field2; 
    bool field3; 
    std::string field4; 
}; 

// Type-Specific Adapter API 

template <typename T> struct GadgetTraits; 

struct Example1Gadget : Example1 { 
    Q_GADGET 
    Q_PROPERTY(int field1 MEMBER field1) 
    Q_PROPERTY(double field2 MEMBER field2) 
    Q_PROPERTY(bool field3 MEMBER field3) 
    Q_PROPERTY(QString field4 READ getField4 WRITE setField4) 
public: 
    void setField4(const QString & val) { field4 = val.toStdString(); } 
    QString getField4() const { return QString::fromStdString(field4); } 
}; 
template <> struct GadgetTraits<Example1> { using gadget_type = Example1Gadget; }; 

// Generic Adapter API 

template <typename T> 
void set(T & gadget, const QStringList & data) 
{ 
    for (auto & entry : data) set(gadget, entry); 
} 

template <typename T> 
void set(T & gadget, const QString & data) 
{ 
    const QMetaObject & mo = GadgetTraits<T>::gadget_type::staticMetaObject; 
    auto const fields = data.split('='); 
    if (fields.length() != 2) return; 
    auto field = fields[0].trimmed(); 
    auto value = fields[1].trimmed(); 
    auto i = mo.indexOfProperty(field.toUtf8()); 
    if (i < 0) return; 
    auto prop = mo.property(i); 
    prop.writeOnGadget(&gadget, value); 
} 

int main() { 
    Example1 ex; 
    set(ex, {"field1 = 3", "field2 = 1.5", "field3 = true", "field4 = foo bar"}); 
    Q_ASSERT(ex.field1 == 3); 
    Q_ASSERT(ex.field2 == 1.5); 
    Q_ASSERT(ex.field3 == true); 
    Q_ASSERT(ex.field4 == "foo bar"); 
} 
#include "main.moc" 
+0

嗯,我想可以从实际的数据类型派生出小工具适配器,然后将其转换为适配器以避免复制 –

+0

甚至更​​好:使用特性并且不需要投入任何东西:) –

+0

啊,很好的编辑,几乎就像从QtScript时代的原型方法。 –

相关问题