简体   繁体   中英

Template classes C++ / Qt

I have an application which will be receiving messages from another application. These messages will be XML fomatted strings, and they will contain a <messageType> tag. The message type will identify this message as a type of internal message. The following code shows my internal message structures.

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;
};

}

alignment and calibration are the two internal message structures.

What I'm trying to do is build a 'message interpreter' which will receive an XML string, decode it and return any one of the structs shown above; so if the <messageType> is 'alignment', the message interpreter will build an alignment struct, and return that.

So ultimately, I'm trying to make a template function, which can return an arbitrary struct, based on what i read in from <messageType> .

Are my objectives clear? is my approach the right one?

Let me know if I should clarify, or if I should take a different approach.

I don't believe a template function makes sense. Your input is always going to be a string, and C++ can't differentiate function signatures based on return type alone - so I don't know how a template would help - what would the type argument be?

I'd suggest making your function a normal one that parses out the messageType and allocates a struct based on it - you can use whatever constructs you want for this.

The trick would be (in my mind) to derive all of your internal-message-classes from the same empty base class - you could then return a pointer to that base class back from your function, and it will hold whatever type got created.

It be a good idea to return an enumeration along with the pointer in a std::pair which you can use to determine the correct derived type that was created, that way you can cast the result directly to the correct derived type with a static_cast.

As I understand it your structures are known within the application, so what about this save variant:

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_;
}

(coded without verification to give you the idea)

This works without polymorphism (I use this pattern in a compiler for a LISP like language, where polymorphic trees would result in more complicated code). You can change it to return "alignment_x()" and so on, if you like that more.

Fully dynamic structures are not possible, and solutions that try to come near will be rather complicated. Use the most-maintainable solution.

If you write a factory function/functor for each type, you can associate that with the messageType ( map<string, Factory*> will be sufficient), but what to return?

You can return some kind of discriminated union, or boost::variant , if you don't mind the top-level decoder depending on all possible message types.

But, what is the decoder going to do with this return value? If it just switches on the type and calls a type-specific callback in each case, you could invert control by attaching a callback function/functor to the factory directly.

Then the decoder doesn't return anything, it just constructs the message struct and passes it directly to a handler.

Simple implementation (OK, that was more typing than I thought):

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);
}

using QMetaObject::newInstance, so you can create a QObject* that can be converted afterwards to your class using dynamic_cast

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

then, in your XML Parsing Code:

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

And things will work out.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM