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.