Template classes C++ / Qt

Template classes C++ / Qt

本文关键字:Qt C++ classes Template      更新时间:2023-10-16

我有一个应用程序,该应用程序将接收来自另一应用程序的消息。这些消息将是XML格式的字符串,并且它们将包含一个<messageType>标记。消息类型会将此消息标识为内部消息的类型。下面的代码显示了我的内部消息结构。

namespace
Application1{
enum ApplicationAttributes{
    ApplicationName = 1000,
    Start,
    Stop,
    Pause,
    Save,
    Discard,
    SelectRunway,
    DoAlignment,
    RedoAlignment,
    AlignmentOK,
    DoCalibrationStage1,
    SetCalibrationStage1,
    SetCalibrationStage2,
    SetCalibrationStage3,
    CancelCalibration,
    CalibrationOK
};

struct Alignment{
    int x;
    int y;
    int error;
};
struct Calibration{
    int x;
    int y;
    int error;
};
}

对齐和校准是两种内部消息结构。

我想做的是构建一个"消息解释器",它将接收一个XML字符串,对其进行解码,并返回上面显示的任何一个结构;因此,如果<messageType>是"alignment",则消息解释器将构建一个alignment结构,并返回该结构。

因此,最终,我将尝试制作一个模板函数,它可以根据我从<messageType>中读取的内容返回任意结构。

我的目标明确吗?我的方法正确吗?

让我知道我是否应该澄清,或者我是否应该采取不同的方法。

我认为模板函数没有意义。你的输入总是一个字符串,C++不能仅根据返回类型来区分函数签名——所以我不知道模板会有什么帮助——类型参数是什么?

我建议让你的函数成为一个普通的函数,解析messageType并基于它分配一个结构——你可以为此使用任何你想要的结构。

诀窍是(在我看来)从同一个空基类派生所有内部消息类——然后你可以从函数返回一个指向该基类的指针,它将保存创建的任何类型。

最好在std::对中返回一个枚举和指针,您可以使用该对来确定创建的正确派生类型,这样您就可以使用static_cast将结果直接转换为正确的派生类型。

据我所知,您的结构在应用程序中是已知的,那么这个保存变体呢:

class Message {
public:
    static Message Alignment (alignment_t const &);
    ...
    Type type() const;
    int alignment() const;
private:
    Message (Type t);
    assert_type (Type t, const char *msg) const;
private:
   Type type_;
};
Message Message::Alignment (alignment_t const &alignment) 
{
    Message ret (Type::Alignment);
    ret.alignment_ = alignment;
    return ret;
}
void Message::assert_type (Type t, const char *msg) const
{
    if (type() != t) throw std::runtime_error (msg);
}
int Message::alignment() const
{
    assert_type (Type::Alignment, 
                 "alignment_x() called for non-alignment-message");
    return alignment_;
}

(无需验证即可编码,让您了解想法)

这在没有多态性的情况下工作(我在类似LISP语言的编译器中使用这种模式,多态树会导致更复杂的代码)。如果你更喜欢的话,你可以将其更改为返回"alignment_x()"等等。

完全动态的结构是不可能的,试图接近的解决方案将相当复杂。使用最易维护的解决方案。

如果您为每种类型编写一个工厂函数/函子,您可以将其与messageType相关联(map<string, Factory*>就足够了),但返回什么?

如果您不介意根据所有可能的消息类型使用顶级解码器,则可以返回某种有区别的并集或boost::variant

但是,解码器将如何处理这个返回值?如果它只是打开类型并在每种情况下调用特定于类型的回调,则可以通过将回调函数/函子直接附加到工厂来反转控制。

然后解码器不返回任何内容,它只是构造消息struct并将其直接传递给处理程序。

简单的实现(好吧,这比我想象的要打字多):

class Decoder
{
public:
  virtual ~Decoder();
  virtual void decode(std::string const &xml) = 0;
};
template <typename Factory, typename Callback>
class SimpleDecoder: public Decoder
{
  Factory factory;
  Callback callback;
public:
  SimpleDecoder(Factory f, Callback c)
  : factory(f), callback(c)
  {}
  void decode(std::string const &xml)
  {
    callback( factory( xml ) );
  }
};
std::map<std::string, Decoder*> factories;
template <typename F, typename C>
void registerSimpleDecoder(std::string const &n, F f, C c)
{
  factories[n] = new SimpleDecoder(f, c);
}
void decodeXmlMessage(std::string const &messageType, std::string const &body)
{
  factories[messageType]->decode(body);
}

使用QMetaObject::newInstance,这样您就可以创建一个QObject*,之后可以使用dynamic_cast 将其转换为您的类

class MyClass : public QObject{
    public:
    enum Type{ MyClassType = UserType + 1 }
    Q_INVOKABLE MyClass();
}
Q_DECLARE_METATYPE ( MyClass )

然后,在您的XML解析代码中:

MyClass* myObject = (MyClass*) QMetaType::construct ( MyClass::MyClassType );

事情会解决的。