コールバックについて

[戻る]
+ 2007/10/11(木) 14:40:49
C/C++問題の気がするが、VC7.0でWinXPなのでここに質問です。

以下のソース例で、BクラスからAクラス(複数)のコールバックを呼んだ際に
目的のAクラスと違うインスタンスを指す時があり困っています。

ソース例では25番目を指定しますが、これが18番目になったりします。

typedef void(__stdcall *CallBack)(int) ;

class A
{
public:
  A(void);
  virtual ~A(void);
  static void __stdcall Function(int id){ TRACE( "%X" , this ); }
};

class B
{
public:
  B(void);
  virtual ~B(void);
  void Set( int id , CallBack p ){m_ID=id; m_CallBack=p}
  void Execute(){ (*m_CallBack)(m_ID) ; }
protected:
  int m_ID ;
  CallBack m_CallBack ;
};


int main()
{
  A tmpA[50] ;
  B tmpB ;

  tmpB.Set( 100 , tmpA[25].Function ) ;
  tmpB.Execute() ;
}

+ 2007/10/11(木) 14:47:14
すこし間違い。
int main()
{
  A *tmpA = new A [50] ;
  B tmpB ;

  tmpB.Set( 100 , (*tmpA)[25].Function ) ;
  tmpB.Execute() ;
}

シャノン 2007/10/11(木) 15:06:05
static 関数の中で this 参照できるん?

επιστημη [HomePage] 2007/10/11(木) 15:07:27
>  static void __stdcall Function(int id){ TRACE( "%X" , this ); }

staticメンバ関数がthisを参照できっこないんですけど...

επιστημη [HomePage] 2007/10/11(木) 15:08:04
ぁぅ、モロかぶり orz

+ 2007/10/11(木) 15:24:40
すみません。
あまり関係ないかな?と省略してました(^^;

class A
{
(略)
protected:
  static A *thisPointer ;
};
で

A *A::thisPointer = NULL ;
A::A(void)
{
  A::thisPointer = this ;
}

void __stdcall A::Function(int id)
{
  TRACE( "%X" , A::thisPointer );
}

です。

YuO [E-Mail] 2007/10/11(木) 15:27:18
staticはクラスで共有されることを理解していますか?
クラスAで最後にコンストラクタが呼び出されたインスタンスへのポインタ
が,thisPointerに保持されますよ。

Blue 2007/10/11(木) 16:01:48
>A::Function(int id)
idってのがまた関連していそうだけど、、、

こんなことをやりたいのかと想像してみる。

#include <map>
#include <cstdio>

class A
{
    static std::map<int, A*> m_list;

    int m_id;
public:
    A()
    {
        this->m_id = m_list.size();
        m_list[this->m_id] = this;
    }

    int GetID() const { return this->m_id; }
    void Print() const { printf("%d : %p\n", this->m_id, (void*)this); }

    static A* GetPointer(const int id)
    {
        std::map<int, A*>::iterator it = m_list.find(id);
        if (it != m_list.end())
            return it->second;
        return NULL;
    }
};

std::map<int, A*> A::m_list;

int main()
{
    A a1, a2, a3;
    A* p;

    printf("%p\n", &a1);
    p = A::GetPointer(a1.GetID());
    p->Print();

    printf("%p\n", &a2);
    p = A::GetPointer(a2.GetID());
    p->Print();

    printf("%p\n", &a3);
    p = A::GetPointer(a3.GetID());
    p->Print();

    return 0;
}

PATIO 2007/10/11(木) 16:12:46
そもそも、staticなメンバー関数はクラスインスタンス無しで呼べて
かつ、クラスのメンバー変数にアクセスできないわけですから
インスタンス毎に呼び分けても意味が無いのではないかなぁ。

typedef void(__stdcall *CallBack)(int, A*);

static void __stdcall Function(int id, A* pa);
とかしてその都度インスタンスを渡すならわからなくはないですけどね。

class B
{
public:
  B(void);
  virtual ~B(void);
  void Set( int id , CallBack p, A* pa){m_ID=id; m_CallBack=p; m_pa=pa;}
  void Execute(){ (*m_CallBack)(m_ID) ; }
protected:
  int m_ID ;
  A* m_pa
  CallBack m_CallBack ;
};

tmpB.Set( 100 , A::Function, &tmpA[25] ); 

とか。
何がやりたくてこういう事をしているのかまで書くと
もっと色々レスがつくと思いますよ。

Blue 2007/10/11(木) 16:13:16
あ、削除に関してまったく考慮していなかったです。

デストラクタでリストから削除しないとだめですね。
ということでIDの振り方も問題ありです。

PATIO 2007/10/11(木) 16:13:44
ごめん、ここ修正。

  void Execute(){ (*m_CallBack)(m_ID, m_pa) ; }

動くかどうかの検証はしていないので参考程度に。

Kerry 2007/10/11(木) 16:33:19
こうすればどうだろうか? 同じことではあるが…

class A
{
public:
    void Function(int id)
    {}
};

typedef void (A::*Callback)(int);

class B
{
public:
    void Set(int id, Callback p, A* pa)
    {
        m_ID = id;
        m_Callback = p;
        m_pa = pa;
    }
    void Execute()
    {
        (m_pa->*m_Callback)(m_ID);
    }

protected:
    int m_ID;
    A* m_pa;
    Callback m_Callback;
};

tmpB.Set(100, &A::Function, &tmpA[25]);

+ 2007/10/11(木) 18:17:19
レスありがとうございます。
Blue氏提示の方法に変更できるのか、ソースとにらめっこしてました。

>なにがやりたいのか。
まず質問の経緯です。
・classAと、classBは参照関係が全く無い。
・が、classBの状態変化を、特定のclassAインスタンスへ通知する必要がでてきた。
・でもclassAと、classBは結び付けたくない。
・そこでAも、Bも知ってる人(掲題ソースではmain)を新たに作って必要な関数だけを
登録。

と考え、修正してみたのですがバグにはまった次第です。
classBから直接、classAの関数呼んでまうのが簡単ですが、コールバックで実現できな
いかな。
などと欲張ったあたりが敗因?

subaru 2007/10/11(木) 20:51:27
Observerパターンでもいいと思います。

class Callback
{
public:
    Callback() {}
    virtual ~Callback() {}
    virtual void Function(int id) = 0;
};

class A : public Callback
{
public:
    A() {}
    virtual ~A() {}
private:
    void Function(int id) { TRACE( "%X" , this ); }
};

class B
{
public:
    B() : m_ID(0), m_CallBack(NULL) {}
    virtual ~B() {}
    void Set(int id, Callback* p) { m_ID=id; m_CallBack=p; }
    void Execute() { if(m_CallBack) m_CallBack->Function(m_ID); }
private:
    int m_ID ;
    Callback* m_CallBack ;
};

int main()
{
    A tmpA[50] ;
    B tmpB ;

    tmpB.Set( 100 , &tmpA[25] ) ;
    tmpB.Execute() ;
}

PATIO 2007/10/12(金) 12:45:34
subaruさんのが一番素直かなぁ。
私もこの方法に一票。
class Bがclass Aをまったく知らないと言うのは無理があると思いますよ。
せめて親の型くらいは知っていないと呼び出せませんし。

+ 2007/10/12(金) 15:59:31
[[解決]]
subaruさんの方法で検討しようと思います。
ありがとうございました。

古いスレッドには追加発言できません。