【问题标题】:Qt & C++: Array of function pointers with different return and parameter typesQt & C++:具有不同返回和参数类型的函数指针数组
【发布时间】:2019-04-12 21:47:39
【问题描述】:

我正在开发 C++ 11、Qt 5.12。我需要将函数指针存储在某个映射或数组中。关键是,这些方法有不同的返回类型和参数类型。

我在类中定义了一些简单的数据结构。这是一个尽可能简单的示例:

class TableData
{
public:
    enum EnumFieldType
    {
        FieldType_None = 0,
        FieldType_Int,
        FieldType_QString
    };
    enum EnumFieldId
    {
        FieldId_None = 0,
        FieldId_Id,
        MaxSystemFieldId = 64
    };
int id() const;
void setId(const int _id);
QVariant getFieldValue(int _fieldId);
void setFieldValue(int _fieldId, QVariant _value)
protected:
int id_;
//Here some QMap or QVector with the function pointers
}

class EmployeeData : public TableData
{
public: 
    enum EnumFieldId
    {
        FieldId_Name = TableData::MaxSystemFieldId + 1,
        FieldId_Age
    };
   QString name() const;
   int age() const;
   void setName(const QString &_name);    
   int setAge(const Int &_age);    
protected:
    int             age_;
    QString         name_;
}

想法是在TableData中创建三个地图:

  • QMap mapFieldTypes
  • QMap mapGetters
  • QMap

这些映射将填充到 TableData 子类的构造函数中。因此,一旦构建了子类的实例,我就可以使用:

EmployeeData  ed;
ed.setId(5);
ed.setAge(11);
ed.setName("Son Gohan");

或者:

EmployeeData ed;
ed.setField(EmployeeData::FieldId_Id, 5);
ed.setField(EmployeeData::FieldId_Age, 11);
ed.setField(EmployeeData::FieldId_Name, "Son Gohan");

最后这些调用转到 TableData::SetField 方法,该方法应使用 FieldId 键查看映射 mapFieldTypes 以获得字段的类型;然后获取 setter 函数指针并使用正确转换为 setter 函数请求的类型的 QVariant 参数调用它。

我花了很多时间来解决这个问题,阅读、寻找和寻找一些解决方案。指针或回调数组必须是相同的函数类型,std::function 和 std::bind 不能解决这个问题(数组必须是相同的函数类型并且绑定使用已经预定义的值等)......我需要类似的东西到应用于变量的 (void *) 指针,但对于函数指针(使用类型映射,我认为我可以有效地将泛型函数指针转换为所需的函数指针模板)。

有什么想法吗?

PD:对于一些代码问题或拼写错误,我们深表歉意,真正的代码要详细得多,我已尝试尽可能简化和简化它。

【问题讨论】:

    标签: c++ qt callback


    【解决方案1】:

    看起来Qt's Property System 是您的完美解决方案:

    class TableData : public QObject {
        Q_OBJECT
    
        public:
            TableData() : QObject(), _id(0) {}
    
            QVariant getFieldValue(const char * fieldName) const {
                return property(fieldName);
            }
    
            void setFieldValue(const char * fieldName, QVariant _value) {
                setProperty(fieldName, _value);
            }
    
            int id() const { return _id; }
    
            void setId(int newID) {
                _id = newID;
                emit idChanged();
            }
    
        protected:
            Q_PROPERTY(int id
                       READ id
                       WRITE setId
                       NOTIFY idChanged)
            int _id;
    
        signals:
            void idChanged();
    };
    
    class EmployeeData : public TableData {
        Q_OBJECT
    
        public:
            EmployeeData() : TableData(), _age(-1), _name("") {}
    
            int age() const { return _age; }
    
            void setAge(int newAge) {
                _age = newAge;
                emit ageChanged();
            }
    
            QString name() const { return _name; }
    
            void setName(QString newName) {
                _name = newName;
                emit nameChanged();
            }
    
        protected:
            Q_PROPERTY(int age
                       READ age
                       WRITE setAge
                       NOTIFY ageChanged)
            int _age;
    
            Q_PROPERTY(QString name
                       READ name
                       WRITE setName
                       NOTIFY nameChanged)
            QString _name;
    
        signals:
            void ageChanged();
            void nameChanged();
    };
    

    void TableData::setField(const char *, QVariant); 方法将调用void QObject::setProperty(const char *, QVariant);。最后一个将自动调用您在WRITE 属性字段中指定的设置器,以更新属性值。以下三段代码完全相同:

    使用属性的设置器:

    EmployeeData ed;
    ed.setId(5);
    ed.setAge(11);
    ed.setName("Son Gohan");
    

    使用void TableData::setField(const char *, QVariant);

    EmployeeData ed;
    ed.setField("id", 5);
    ed.setField("age", 11);
    ed.setField("name", "Son Gohan");
    

    使用void QObject::setProperty(const char *, QVariant);(因为你的EmployeeData也是QObject):

    EmployeeData  ed;
    ed.setProperty("id", 5);
    ed.setProperty("age", 11);
    ed.setProperty("name", "Son Gohan");
    

    读取属性也是如此。您的QVariant TableData::getField(const char *) const; 方法将调用QVariant QObject::property(const char *) const;。最后一个将自动调用您在READ 属性字段中指定的getter,以检索属性值。以下三段代码完全相同:

    使用属性的设置器:

    // Using the EmployeeData ed defined above
    int id = ed.id();            // id == 5
    int age = ed.age();          // age == 11
    QString name = ed.name();    // name == "Son Gohan"
    

    使用QVariant TableData::getField(const char *) const;

    // Using the EmployeeData ed defined above
    int id = ed.getField("id").toInt();               // id == 5
    int age = ed.getField("age").toInt();             // age == 11
    QString name = ed.getField("name").toString();    // name == "Son Gohan"
    

    使用QVariant QObject::property(const char *) const;(因为你的EmployeeData也是QObject):

    // Using the EmployeeData ed defined above
    int id = ed.property("id").toInt();               // id == 5
    int age = ed.property("age").toInt();             // age == 11
    QString name = ed.property("name").toString();    // name == "Son Gohan"
    

    【讨论】:

    • es,我认为这种方法符合我的需要。我想让 TableData 类尽可能简单,但是这种方法和 QObject+QPropertiy 元系统的好处显然超过了缺点。我将重构我所有的数据结构......一堆工作,但我将值得付出努力。谢谢!! PS:但我仍然很好奇...... ¿是否有可能获得具有不同返回值或参数的函数指针数组(或类似数组)? PS2:我喜欢你在编写示例时所付出的努力......竖起大拇指
    • 如果您的类派生自 QObject,您还可以使用 Qt 的自省 API,以及元对象和 QMeta* 类。它可能不是你想要的函数指针,但它仍然可以让你访问方法并调用它们。
    猜你喜欢
    • 2016-09-13
    • 2015-04-18
    • 2014-05-12
    • 1970-01-01
    • 1970-01-01
    • 2017-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多