0

ここに私のメッセージ構造があります:

struct tEventMessage 
{
    // Type of the event
    int Type;

    // (void*) Allows those to be casted into per-Type objects
    void *pArgument1;   
    void *pArgument2;

};

この構造体にある種の「テンプレート」メンバーを追加して、後でメッセージを作成するときに、これらのポインター + と必要なその他のデータを渡すことができますか? (以下の例を参照)

struct tEventMessage 
{
    // Type of the event
    int Type;

    // (void*) Allows those to be casted into per-Type objects
    void *pArgument1;   
    void *pArgument2;

    // Template
    T tSomeTemplateMember;
};

 void HandleClick(....)
 {
 CVector3 vNewPosition = ....

 tEventMessage _msg;
 _msg.Type = xxxx;
 _msg.pArgument1 = pA->GetObjectPointer();

 //
 // Wrong!
 // Because this CVector3 will not be alive in next tick
 // - my pointer will point to nothing.
 //
 _msg.pArgument2 = static_cast<CVector3*>(&vNewPosition)


 //
 // Something like that would be great
 // And would allow me to use CVector2,CVector3,CVector4 with one template member
 // 
 _msg.tSomeTemplateMember = vNewPosition;

 }
4

2 に答える 2

2

問題を複雑にしすぎていると思います。メッセージで任意のデータを渡す方法という 1 つの問題ではなく、テンプレートの扱い方という 2 つの問題があります。

この種のものを実装する通常の方法は、継承を使用することです:-

class Message
{
public:
  int Type () { return type; }
protected:
  int type;
};

class ClickMessage : public Message
{
public:
  ClickMessage () { type = ClickMessageID; }
private:
  // the message data
};

void HandleMessage (Message *message)
{
  switch (message->Type ())
  {
  case ClickMessageID:
    HandleClick (reinterpret_cast <ClickMessage *> (message));
    break;
  default:
    // unhandled message error
    break;
  }
}

void HandleClick (ClickMessage *message)
{
  // do stuff
}

問題は、switch ステートメントでのキャストなど、多くのコードを繰り返すことになることです。また、メンテナンスの問題もあります。新しいメッセージを追加するには、少し慎重に更新する必要があります。コードを少しハックして、関数ポインターとマップを使用してメッセージ タイプを関数に変換し、switch ステートメントを置き換えることができます。

巧妙なテンプレート ソリューションがあるかもしれませんが、それが何であるかはわかりません。

RTTI を使用すると、(費用はかかりますが) 役立つ場合があります。

これは、リフレクションが解決するのに非常に優れた問題の 1 つです。

于 2012-11-01T21:37:03.897 に答える
1

おそらく私は何かを見逃していますが、なぜ抽象クラスから始めて、そこからさまざまな種類のイベント メッセージを派生させないのか疑問に思っています。抽象クラスを利用し、そこからクラスを派生させることで、switch ステートメントを使用しているロジックをコンパイラに理解させます。このC++ ポリモーフィズムと抽象基本クラスのチュートリアルを参照してください。

これもMSDN on Abstract classesから参照してください。

たとえば、次のような抽象クラスがあるとします。ただし、これほど多くは必要なく、実際には単一のprocessEvent()メソッドのみが必要な場合があります。派生クラスは、抽象クラスで指定された各関数の独自のバージョンを提供する必要があります。

class EventMessage abstract {
public:
    virtual void *getArgument1 (void) = 0;
    virtual void *getArgument2 (void) = 0;
    virtual int   processEvent (void) = 0;
protected:
    void *pArgument1;
    void *pArgument2;
};

この抽象クラスが定義するのは、実際のメッセージを処理するために呼び出されるメソッドとともに、さまざまなイベント メッセージのすべてで使用されるデータを基本的に含むクラスです。クラス自体はインスタンス化されませんが、実際にオブジェクトとしてインスタンス化される他の派生クラスの親またはスーパー クラスとして使用されます。

次に行うことは、EventMessage インターフェイスを実装する新しいクラスを派生させることです。たとえば、これを行う 2 つの異なるクラスを次に示します。

class JoJoEvent : public EventMessage {
public:
    JoJoEvent(void *arg1, void *arg2);
    void *getArgument1 (void);
    void *getArgument2 (void);
    int   processEvent (void);
};
JoJoEvent::JoJoEvent(void *arg1, void *arg2)
{
    pArgument1 = arg1;
    pArgument2 = arg2;
}

void * JoJoEvent::getArgument1 (void) {
    return pArgument1;
}
void * JoJoEvent::getArgument2 (void) {
    return pArgument2;
}

int JoJoEvent::processEvent (void) {
    // do stuff with the arguments
    return 1;
}

class KoKoEvent : public EventMessage {
public:
    KoKoEvent(void *arg1, void *arg2);
    void *getArgument1 (void);
    void *getArgument2 (void);
    int   processEvent (void);
};
KoKoEvent::KoKoEvent(void *arg1, void *arg2)
{
    pArgument1 = arg1;
    pArgument2 = arg2;
}

void * KoKoEvent::getArgument1 (void) {
    return pArgument1;
}
void * KoKoEvent::getArgument2 (void) {
    return pArgument2;
}

int KoKoEvent::processEvent (void) {
    // do stuff with the arguments
    return 1;
}

次に、これらを使用するときは、次のコードのようにします。

EventMessage *myMessage = new JoJoEvent(0, 0);

EventMessage *myMessage2 = new KoKoEvent(0, 0);

myMessage2->processEvent();
myMessage->processEvent();

派生クラスにデータを追加する必要がある場合は、データを派生クラスに入れるメカニズムを提供するだけで実行できます。

于 2012-11-01T23:13:00.070 に答える