Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

Signal/Slot instead of MFC/wxWidgets MSG-MAP

Moderators: larsivi kris

Posted: 09/20/07 16:50:53 Modified: 09/20/07 16:59:53

Hi, I've tried to translate some C++ stuff into D. The D port has Bugs, However the intention should be clear. As you can see I have tried to translate the MSG_MAP macros into D templates. However, the question is, it possible to replace the MSG_MAP code using Tango's Signal/Slot... implementation ?

/* THE C++ Stuff. (Just a fragment. D port a few lines later)

class CMsg { public:

virtual BOOL NewMsgProc?(HWND hWnd, UINT uID, WPARAM wParam, LPARAM lParam,LRESULT lResult) {

return FALSE;

}

};

#define BEGIN_MSG_MAP() \ public: \

virtual BOOL NewMsgProc?(HWND hWnd, UINT uID, WPARAM wParam, LPARAM lParam,LRESULT& lResult) \ { \

#define ON_WM_CLOSE(vfunc)\

if (uID == WM_CLOSE) \ { \

lResult =vfunc(); \

return lResult; \

}

// and so on ...........

class CWin : public CMsg {

public:

virtual BOOL OnClose?() {

return TRUE;

}

BEGIN_MSG_MAP()

ON_WM_CLOSE(OnClose?)

END_MSG_MAP_DEFAULT()

}

EOF C++ stuff */

//THE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; D port

module dgt.win;

// All Window's messages will be mapped in this function

class CMsg { public:

bool NewMsgProc?(HWND hWnd, UINT uID, WPARAM wParam, LPARAM lParam,LRESULT lResult) {

return false;

}

}

template ON_WM_CLOSE(D) (D delegate() dg) {

if (uID == WM_CLOSE) {

lResult = dg(); return lResult;

}

}

template ON_MESSAGE_RANGE(M1, M2, D) (M1 MsgF, M2 MsgL, D delegate(...) dg ) {

if(uID >= MsgF && uID <= MsgL)

{

lResult = dg(uID, wParam, lParam);

return true;

}

} // Window

class CWin : CMsg

{

public:

bool onClose() {

return true;

}

override bool NewMsgProc?(HWND hWnd, UINT uID, WPARAM wParam, LPARAM lParam, ref LRESULT lResult) {

mixin ON_WM_CLOSE!(&CWin.OnClose?);

}

}

Okay the templates have bugs... may be I should use :

template ON_WM_CLOSE(D) {

alias D delegate() dg; if (uID == WM_CLOSE) {

lResult = dg(); return lResult;

}

}

However, the BIG question is, can I use Signal/Slot instead. If yes : How :-) I really need some advice. Thanks

Bjoern

Author Message

Posted: 09/20/07 17:23:28

Signals & Slots are a means of broadcasting an event to multiple listeners. However here, you want to dispatch a message to one specific listener based on an event id. So the basic concept is pretty similar, but isn't exactly what you're looking for. What I'd probably do is make something like this:

struct MultiDispatch(Key)
{
    alias void delegate() Dg;

    void attach( Key k, Dg dg )
    {
        m_map[k] = dg;
    }

    void opCall( Key k )
    {
        auto dg = k in m_map;
        if( dg !is null )
            (*dg)();
    }
private:
    Dg[Key] m_map;
}

class MyClass
{
    void onClose()
    {

    }

    MultiDispatch!(int) signal;

    this()
    {
        signal.attach( WM_CLOSE, &onClose );
    }
}

...

auto c = new MyClass;

c.signal( WM_CLOSE );

Posted: 09/20/07 18:26:27

Smart. And you write it within minutes ? incredible. But what I need is class MyClass? {

void onClose() {

}

MultiDispatch?!(int) signal;

this() {

signal.attach( WM_CLOSE, &onClose );

}

bool NewMsgProc?(HWND hWnd, UINT uID, WPARAM wParam, LPARAM lParam, ref LRESULT lResult)

{

if (uID == WM_CLOSE) {

lResult = vfunc(); // call onClose() set lResult thats why we have a ref return lResult;

}

// next message

}

See : #define ON_WM_CLOSE(vfunc)\

if (uID == WM_CLOSE) \ { \

lResult =vfunc(); \

return lResult; \

}

}

Posted: 09/20/07 18:40:46

yeah, it's a chain of pseudo subclasses calling 'super' :)

Posted: 09/21/07 14:08:37

Hm, so to alter the design slightly:

struct MultiDispatch(Key, Ret)
{
    alias Ret delegate() Dg;

    void attach( Key k, Dg dg )
    {
        m_map[k] = dg;
    }

    Ret opCall( Key k )
    {
        auto dg = k in m_map;
        if( dg !is null )
            return (*dg)();
        return Ret.init;
    }
private:
    Dg[Key] m_map;
}

class MyClass
{
    void onClose()
    {

    }

    MultiDispatch!(int,LRESULT) dispatch;

    this()
    {
        signal.attach( WM_CLOSE, &onClose );
    }

    bool NewMsgProc( HWND hWnd, UINT uID, WPARAM wParam, LPARAM lParam, ref LRESULT lResult )
    {
        return signal.dispatch( uID );
    }
}

The alternative to make things even more like a macro would be to use a mixin, but I'm not terribly fond of them :-) Still, I suppose it would be something like this:

template Mixin_MultiDispatch(Key, Ret)
{
    alias Ret delegate() Dg;

    void attach( Key k, Dg dg )
    {
        m_map[k] = dg;
    }

    bool NewMsgProc( HWND hWnd, UINT uID, WPARAM wParam, LPARAM lParam, ref LRESULT lResult )
    {
        auto dg = uID in m_map;
        if( dg !is null )
            return (*dg)();
        return Ret.init;
    }
private:
    Dg[Key] m_map;
}

class MyClass
{
    void onClose()
    {

    }

    mixin Mixin_MultiDispatch!(int,LRESULT);

    this()
    {
        attach( WM_CLOSE, &onClose );
    }
}

If you take this route you may have to use the more complicated:

    mixin Mixin_MultiDispatch!(int) dispatch;
    alias dispatch.NewMsgProc NewMsgProc;

    ...

    dispatch.attach( WM_CLOSE, &onClose );

To have NewMsgProc? show up properly as a proper member of MyClass?. I believe this used to be the case, but it's been a while since I played with mixins.

Finally, it may be possible to initialize things once by making the dispatch mechanism static and passing in 'this' when an event is signaled. I can't think of an easy way to get delegates to insert without creating an instance of MyClass? however, so either a temporary one would have to be created or perhaps some dirty tricks are possible to get the address of the relevant functions. In short, it would be a bunch of extra machinery for potentially little gain :-)