По мотивам SWL
От: WolfHound  
Дата: 26.04.03 20:06
Оценка: 29 (3) +1
Сейчас наткнулся на SWL идея там на 5+ но реализация на 3-
Наброски моего варианта
#include <windows.h>
#include <Loki\AssocVector.h>
struct WndMsg
{
    const UINT    id;
    WPARAM        wprm;
    LPARAM        lprm;
    LRESULT        res;
    bool        handled;
public:
    WndMsg(UINT msg, WPARAM wp, LPARAM lp)
        :id(msg)
        ,wprm(wp)
        ,lprm(lp)
        ,res(0)
        ,handled(false)
    {}
};
struct Wnd
{
    virtual void MsgHandle(WndMsg* msg)
    {
    }
    virtual ~Wnd(){}
};
struct Msg_
{
    WPARAM&        wprm;
    LPARAM&        lprm;
    LRESULT&    res;
    bool&        handled;
    Msg_(WndMsg* msg)
        :wprm(msg->wprm)
        ,lprm(msg->lprm)
        ,res(msg->res)
        ,handled(msg->handled)
    {}
};
template<UINT id>
struct Msg:Msg_
{
    Msg(WndMsg* msg)
        :Msg_(msg)
    {}
};
class Handler
{
    struct SortedHandlers
    {
        typedef Loki::AssocVector<UINT, Handler*>    MapType;
        typedef MapType::iterator                    MapTypeIter;
        MapType handlers_;
    public:
        template<size_t N>
        SortedHandlers(Handler(&mmp)[N])
        {
            for(int i=0;i<N;++i)
                handlers_.insert(MapType::value_type(mmp[i].id_, &mmp[i]));
        }
        Handler* GetHandler(UINT id)
        {
            MapTypeIter iter=handlers_.find(id);
            if(iter==handlers_.end())return 0;
            return iter->second;
        }
    };
    struct CallProxyBase
    {
        virtual void Call(WndMsg*, Wnd*)=0;
        virtual ~CallProxyBase(){}
    };
    template<UINT id, class T>
    struct CallProxy
        :CallProxyBase
    {
        typedef void (T::*MfnType)(Msg<id>&);
        MfnType fn_;
        CallProxy(MfnType fn)
            :fn_(fn)
        {}
        void Call(WndMsg* msg, Wnd* wnd)
        {
            (static_cast<T*>(wnd)->*fn_)(Msg<id>(msg));
        }
    };
    CallProxyBase* proxy_;
public:
    const UINT id_;
    template<UINT id, class T>
    Handler(void (T::*fn)(Msg<id>&))
        :id_(id)
    {
        static CallProxy<id, T> proxy(fn);
        proxy_=&proxy;
    }
    template<class T, size_t N>
    static bool Call(Handler(&mmp)[N], T* wnd, WndMsg* msg)
    {
        static SortedHandlers handlers(mmp);
        if(Handler* handler=handlers.GetHandler(msg->id))
        {
            handler->proxy_->Call(msg, wnd);
            return msg->handled;
        }
        return false;
    }
};
struct Test:Wnd
{
    void f1(Msg<1>&){printf("1\n");}
    void f2(Msg<2>&){printf("2\n");}
    void f3(Msg<3>&){printf("3\n");}
    void f4(Msg<4>&){printf("4\n");}
    void OnPaint(Msg<WM_PAINT>& msg){printf("WM_PAINT\n");msg.handled=true;}
    void MsgHandle(WndMsg* msg)
    {
        static Handler mmp[]=
        {
            &Test::f1,
            &Test::f2,
            &Test::f3,
            &Test::f4,
            &Test::OnPaint,
        };
        if(!Handler::Call(mmp, this, msg))
            Wnd::MsgHandle(msg);
    }
};
int _tmain(int argc, _TCHAR* argv[])
{
    Test test;
    WndMsg msg(WM_PAINT, 0, 0);
    test.MsgHandle(&msg);
    return 0;
}
... << RSDN@Home 1.0 beta 6a >>

28.04.03 10:37: Перенесено из 'C/C++'
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re: По мотивам SWL
От: yaroslav_v http://yaroslav-v.chat.ru
Дата: 27.04.03 18:17
Оценка:
Интересная идея, я бы еще добавил сортировку не только по номерам сообщений,
но и по кодам — типа как работает COMMAND_HANDLER_EX(), COMMAND_CODE_HANDLER_EX() и т.д.
Как это сделать расширяемо и лаконично — вопрос.
Re: По мотивам SWL
От: Kluev  
Дата: 03.05.03 16:43
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Сейчас наткнулся на SWL идея там на 5+ но реализация на 3-


А почему на 3- ? Вы наверное имеели в виду, что используется неассоциативный поиск по обработчикам?
На самом деле используется двоичный поиск в предварительно отсортированном масиве — это тоже очень быстро и никаких динамических выделений
Re[2]: По мотивам SWL
От: Kluev  
Дата: 03.05.03 16:48
Оценка:
Здравствуйте, yaroslav_v, Вы писали:

_>Интересная идея, я бы еще добавил сортировку не только по номерам сообщений,

_>но и по кодам — типа как работает COMMAND_HANDLER_EX(), COMMAND_CODE_HANDLER_EX() и т.д.
_>Как это сделать расширяемо и лаконично — вопрос.

Я уже потом понял что функционалал связанный с поиском обработчиков можно вынести за пределы базового класса. В этом случае можно использовать многоуровневую диспетчеризацию т.е. одна карта в WndProc для сообщений первого уровня, в обработчике WM_COMMAND используется карта для поиска обработчиков по ID и в WM_NOTIFY то же самое.

Будет время на досуге сделаю
Re[2]: По мотивам SWL
От: WolfHound  
Дата: 03.05.03 16:57
Оценка:
Здравствуйте, Kluev, Вы писали:

K>А почему на 3- ? Вы наверное имеели в виду, что используется неассоциативный поиск по обработчикам?

Не я это понял. Но там много тонких ошибок которые рано или позно выстрелят. Много лишнего кода. Все ошибки перечислять очень долго...
... << RSDN@Home 1.0 beta 6a >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[3]: По мотивам SWL
От: Kluev  
Дата: 04.05.03 07:41
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Не я это понял. Но там много тонких ошибок которые рано или позно выстрелят. Много лишнего кода. Все ошибки перечислять очень долго...


Раз есть ошибки надо перечислять (Можно в приват)
Re: По мотивам SWL
От: limax Эстония http://mem.ee
Дата: 06.05.03 08:27
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Наброски моего варианта


Сидел, выписывал все использованные С++ трюки. Насчитал штук 5 незнакомых и сразу взял на вооружение. Особенно меня порадовал параметр ссылка на массив с размером по параметру шаблона — вроде совершенно простой, но какой полезный! Правда синтаксис для меня совершенно дикий.

Кстати, при внедрении этого кода в свою библиотеку я избавился от указателей — не нужны они здесь — заменил на ссылки.
Единственное место, где указатель лучше оставить — это GetHandler, но там я сделал следующую конструкцию, чтобы указатель можно было только использовать для доступа к членам:
//! "ссылка" на класс с проверкой.
template <class T> class Ref
  {
  T* _ref; //!< private

  public:
    Ref( T* ptr )
      : _ptr (ptr) {}

    operator bool() const
      { return (_ptr!=NULL); }

    T* operator->()   //!< non-const "reference"
      { assert(_ptr!=NULL); return _ptr; }

    const T* operator->() const  //!< const "reference"
      { assert(_ptr!=NULL); return _ptr; }
  };

//...
class Handler
  {
  struct SortedHandlers
    {
    //...

    //! возвращаемый "указатель" можно только использовать
    //! для доступа к членам и для проверки валидности.
    Ref<Handler> GetHandler( UINT id )
      {
      MapTypeIter iter=handlers_.find(id);
      if(iter==handlers_.end())return 0;
      return iter->second;
      }
    };
  //...
  };


Ещё в одном месте инициализацию ссылки пришлось передвинуть в список инициализации.
class Handler
  {
  CallProxyBase& proxy_;
  //...

  template<UINT id, class T> Handler(void (T::*fn)(Msg<id>&))
    : id_(id)
    //инициализировать ссылку можно только в списке инициализации
    , proxy_ (StaticCallProxy<id, T>(fn))
    {}

  //! статический прокси вынесенный в метод
  template<UINT id, class T> static inline CallProxy&
      StaticCallProxy(void (T::*fn)(Msg<id>&))
    {
    static CallProxy<id, T> proxy(fn);
    return proxy;
    }
  };
Have fun: Win+M, Ctrl+A, Enter
Re: Можно сильнее извратиться....
От: lboss Россия  
Дата: 08.05.03 13:38
Оценка: 27 (3)
#include <windows.h>
#include <map>
#include <assert.h>

class MsgData
{
public:
    HWND        m_hWnd;
    UINT        m_msg;
    WPARAM        m_wParam;
    LPARAM        m_lParam;

    MsgData(HWND _hWnd, UINT _msg, WPARAM _wParam, LPARAM _lParam)
        : m_hWnd(_hWnd), m_msg(_msg), m_wParam(_wParam), m_lParam(_lParam)
    {
    }
    MsgData(const MsgData & o)
        : m_hWnd(o.m_hWnd), m_msg(o.m_msg), m_wParam(o.m_wParam), m_lParam(o.m_lParam)
    {
    }
};

//Это для идеи разширения сообщений
template <UINT msgID>
class Msg : public MsgData
{
public:
    Msg(HWND _hWnd, UINT _msg, WPARAM _wParam, LPARAM _lParam)
        : MsgData(_hWnd, _msg, _wParam, _lParam)
    {
        assert(_msg == msgID);
    }
    Msg(const MsgData & o)
        : MsgData(o)
    {
        assert(o.m_msg == msgID);
    }
    Msg(const Msg & o)
        : MsgData(o)
    {
        assert(o.m_msg == msgID);
    }
};

//Базовый класс всех окон
class Wnd
{
public:
    virtual    bool    processMessage(MsgData & msg)
    {
        return false;
    }
};

//Таблица обработчиков в классе
template <class TRel>
class MsgProcessor : public TRel
{
public:
    static    MsgProcessor<TRel> & getInstance()
    {
        static MsgProcessor<TRel>    m_instance;
        return m_instance;
    }

    typedef bool    (*ProcessMessageFunc)(TRel * obj, MsgData & msg);

    void    registerMsgFunct(UINT mID, ProcessMessageFunc pf)
    {
        m_map[mID] = pf;
    }

    virtual    bool    processMessage(TRel * wnd, MsgData & msg)
    {
        ProcessMessageFunc pf = m_map[msg.m_msg];
        if(pf)
            return pf(wnd, msg);
        else
            return false;
    }
protected:
    std::map<UINT, ProcessMessageFunc>    m_map;
};


//Форсиреет регистрацию обработчиков в MsgProcessor<TRel>
template <class TRel, UINT msgID>
class MsgResult
{
public:
    //Регистратор
    class Initializer
    {
    public:
        Initializer()
        {
            MsgProcessor<TRel>::getInstance().registerMsgFunct(msgID, processMessage);
        }
        static    bool    processMessage(TRel * obj, MsgData & msg)
        {
            return obj->onMsg(Msg<msgID>(msg));
        }
        void    forceInstance()
        {
        }
    };
    static    Initializer m_Initializer;
    MsgResult(bool bRet)
        : m_bRet(bRet)
    {
        //Это чтоб компилятор Initializer::Initializer не выбросил
        m_Initializer.forceInstance();
    }
    operator bool () const { return m_bRet; }
protected:
    bool    m_bRet;
};
template <class TRel, UINT msgID>
typename MsgResult<TRel, msgID>::Initializer MsgResult<TRel, msgID>::m_Initializer;

//Просто пример рассширения Msg для тестирования
template <>
class Msg<WM_PAINT> : public MsgData
{
public:
    Msg(HWND _hWnd, UINT _msg, WPARAM _wParam, LPARAM _lParam)
        : MsgData(_hWnd, _msg, _wParam, _lParam)
    {
        assert(_msg == WM_PAINT);
    }
    Msg(const MsgData & o)
        : MsgData(o)
    {
        assert(o.m_msg == WM_PAINT);
    }
    Msg(const Msg & o)
        : MsgData(o)
    {
        assert(o.m_msg == WM_PAINT);
    }

    void    testOnPaint()
    {
        puts("testOnPaint()");
    }
};


Данный код позволяет писать MessageMap'ы проще:


class Test1 : public    Wnd
{
public:
    MsgResult<Test1, WM_PAINT>    onMsg(Msg<WM_PAINT> & msg)
    {
        puts("MsgResult<Test1, WM_PAINT> Test1::onMsg(Msg<WM_PAINT> & msg)");
        msg.testOnPaint();
        return true;
    }

    //В принципе от этой функции тоже можно избавиться, если писать MsgResult<Текущий класс, msgID, Базовый класс>,
    //но это мне показалось замуженным (хотя можно сделать обе возможности)... 
    virtual    bool    processMessage(MsgData & msg)
    {
        return MsgProcessor<Test1>::getInstance().processMessage(this, msg) || Wnd::processMessage(msg);
    }
};

class Test : public Test1
{
public:
    MsgResult<Test, WM_USER>    onMsg(Msg<WM_USER> & msg)
    {
        puts("MsgResult<Test, WM_USER>    Test::onMsg(Msg<WM_USER> & msg)");
        return true;
    }

    virtual    bool    processMessage(MsgData & msg)
    {
        return MsgProcessor<Test>::getInstance().processMessage(this, msg) || Test1::processMessage(msg);
    }
};


int _tmain(int argc, _TCHAR* argv[])
{
    Test test;
    printf("test.processMessage(MsgData(NULL, WM_PAINT, 0, 0)) = %s\n", test.processMessage(MsgData(NULL, WM_PAINT, 0, 0)) ? "true" : "false");
    printf("test.processMessage(MsgData(NULL, WM_USER, 0, 0)) = %s\n", test.processMessage(MsgData(NULL, WM_USER, 0, 0)) ? "true" : "false");
    printf("test.processMessage(MsgData(NULL, 1, 0, 0)) = %s\n", test.processMessage(MsgData(NULL, 1, 0, 0)) ? "true" : "false");
    getchar();
    return 0;
}


Было бы элегантней если можно было бы писать так:

class Test1 : public    Wnd
{
public:
    bool    onMsg(MsgHook<Test1, WM_PAINT> & msg)
    {
           <Skip>
    }
};


Но я не могу придумать как форсировать вызов конструктора Initializer'а... Пришлось придумывать MsgResult...
С уважением Вадим.
Re[2]: Можно сильнее извратиться....
От: limax Эстония http://mem.ee
Дата: 08.05.03 14:06
Оценка:
Здравствуйте, lboss, Вы писали:
L>Но я не могу придумать как форсировать вызов конструктора Initializer'а... Пришлось придумывать MsgResult...
Интересная методика. Осталось разобраться почему она вообще работает.
...
Ах-ха! Нашёл.
template <class TRel, UINT msgID>
 typename MsgResult<TRel, msgID>::Initializer
  MsgResult<TRel, msgID>::m_Initializer;

А будет оно будет работать с разделением исходника на файлы?
Куда instance этого самого статического члена шаблона ставить?
Have fun: Win+M, Ctrl+A, Enter
Re: По мотивам "Прощания Славянки"
От: c-smile Канада http://terrainformatica.com
Дата: 08.05.03 23:55
Оценка:
Здравствуйте, ГоночныйВолк со товарищи!

А вот у меня вопрос возник:

На кой ляд все эти танцы с бубнами?

Имеем десять WM_*** сообщений которые требуется обрабатывать.
Чего бы просто не написать:

class window() 
{

// primordial events

  virtual bool on_create(...){...}
  virtual bool on_die(...){...}
  virtual bool on_mouse(...){...}
  virtual bool on_key(...){...}
  virtual bool on_focus(...){...}
  virtual bool on_size(gool::size oldsize) {...}
...

// synthesized events
  virtual bool on_command(int code, ...) {...}
  virtual bool on_notify(int code, ...) {...}

  virtual bool on_unknown(uint msg, wparam, lparam...)  

};


И гори оно огнем....

Или в извращениях есть некая прэлэсть которой я не вижу?
Дык ить может я чего теряю в жизни?
Re[2]: По мотивам "Прощания Славянки"
От: WolfHound  
Дата: 09.05.03 07:13
Оценка: +1
Здравствуйте, c-smile, Вы писали:

CS>На кой ляд все эти танцы с бубнами?


По ссылке ходил?
А если коротко то кусок кода SWL
    template <> struct Msg<WM_CREATE> : MsgBase {
        Msg( MsgBase &msg ) : MsgBase(msg) {}
    public:
        CREATESTRUCT& cs() const { 
            return *(LPCREATESTRUCT)lprm; 
        }
    };

    template <> struct Msg<WM_NCCREATE> : MsgBase {
        Msg( MsgBase &msg ) : MsgBase(msg) {}
    public:
        CREATESTRUCT& cs() const { 
            return *(LPCREATESTRUCT)lprm; 
        }
    };

    template <> struct Msg<WM_PAINT> : MsgBase {
        PAINTSTRUCT        ps;
    public:
        Msg( MsgBase &msg ) : MsgBase(msg) {}
    public:
        HDC paint_begin( HWND hwnd ) {
            return ::BeginPaint( hwnd, &ps );
        }

        void paint_end( HWND hwnd ) {
            EndPaint( hwnd, &ps );
        }
    };

    template <> struct Msg<WM_COMMAND> : MsgBase {
        Msg( MsgBase &msg ) : MsgBase(msg) {}
    public:
        HWND hwnd() const { return (HWND)lprm; }
        int id() const { return LOWORD(wprm); }
        int code() const { return HIWORD(wprm); }
    };

    template <> struct Msg<WM_NOTIFY> : MsgBase {
        Msg( MsgBase &msg ) : MsgBase(msg) {}
    public:
        HWND hwnd() { return nmhdr()->hwndFrom; }
        NMHDR* nmhdr() const { return (LPNMHDR)lprm; }
        int id() const { return wprm; }
    };

    template <> struct Msg<WM_LBUTTONDOWN> : MsgBase {
        Msg( MsgBase &msg ) : MsgBase(msg) {}
        
        POINT point() const {
            POINT p = { GET_X_LPARAM(lprm), GET_Y_LPARAM(lprm) };
            return p;
        }

        int code() {
            return wprm;
        }
    };

Испльзования соответственно
    void wm_paint( Msg<WM_PAINT> &msg ) { // Msg<WM_PAINT> теперь вы поняли в чем хитрость?
        HDC hdc = msg.paint_begin( hwnd() );
        
        RECT rc;
        GetClientRect( hwnd(), &rc );
        DrawText( hdc, "Hello SWL!", -1, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
        
        msg.paint_end( hwnd() );
    }

    void wm_close( Msg<WM_CLOSE> &msg ) {
        PostQuitMessage( 0 );
    }

    void wm_create( Msg<WM_CREATE> &msg ) {
        CREATESTRUCT &cs = msg.cs();
        _btnTest.create( "Test", Rect( 20, 20, 80, 40 ), this );    // создадим кнопку
        _btnTest.onClick.set( &MyWindow::btnTest_onClick );            // установим обработчик
    }

А если еще немного помедитировать то получаем
    template<>struct Msg<WM_PAINT> 
        :MsgBase 
    {
        Msg(MsgBase &msg)
            :MsgBase(msg)
            ,paintStarted_(false)
        {}
        ~Msg()
        {
            PaintEnd();
        }
        inline HDC PaintBegin(HWND hwnd)
        {
            hwnd_=hwnd;
            hdc_=BeginPaint(hwnd_, &ps_);
            GetClientRect(hwnd_, &rc_)

            paintStarted_=true;
            return hdc_;
        }
        inline void PaintEnd()
        {
            if(paintStarted_)
            {
                EndPaint(hwnd_, &ps_);
                paintStarted_=false;
            }
        }
        inline RECT& ClientRect()
        {
            return rc_;
        }
        inline HDC Hdc()
        {
            return hdc_;
        }
    private:
        RECT            rc_;
        bool            paintStarted_;
        PAINTSTRUCT        ps_;
        HDC                hdc_;
        HWND            hwnd_;
    };

Расширять можно практически до бесконечности, а главное весь этот код попадает в библиотеку что не может не радовать.

wm_paint превращается в
    void wm_paint(Msg<WM_PAINT>& msg)
    {
        HDC hdc=msg.PaintBegin(hwnd());
        DrawText(
            hdc,
            "Hello SWL!",
            -1,
            &msg.ClientRect(),
            DT_CENTER | DT_VCENTER | DT_SINGLELINE
        );
    }

И так далие.
... << RSDN@Home 1.0 beta 6a >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[2]: По мотивам SWL
От: WolfHound  
Дата: 09.05.03 07:13
Оценка:
Здравствуйте, limax, Вы писали:

L>Сидел, выписывал все использованные С++ трюки. Насчитал штук 5 незнакомых и сразу взял на вооружение.

Хм я и не знал что столько трюков использовал
L>Особенно меня порадовал параметр ссылка на массив с размером по параметру шаблона — вроде совершенно простой, но какой полезный! Правда синтаксис для меня совершенно дикий.
Во завернул
L>Кстати, при внедрении этого кода в свою библиотеку я избавился от указателей — не нужны они здесь — заменил на ссылки.
Тоже вариант
L>Единственное место, где указатель лучше оставить — это GetHandler, но там я сделал следующую конструкцию, чтобы указатель можно было только использовать для доступа к членам:
Хм Не вижу смысла.
L>
L>  T* _ref; //Тут _ref дальше _ptr 
L>

Это мелочи... Но имена _* и __* насколько я помню зарезервированы для разработчиков компилятора.

L>Ещё в одном месте инициализацию ссылки пришлось передвинуть в список инициализации.

L>
L>    , proxy_ (StaticCallProxy<id, T>(fn))
L>

Явно параметры указывать не обязательно компилятор сам найдет.
... << RSDN@Home 1.0 beta 6a >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[2]: По мотивам SWL
От: WolfHound  
Дата: 09.05.03 08:53
Оценка:
Здравствуйте, limax, Вы писали:

L>Сидел, выписывал все использованные С++ трюки.

А вот так можно удавить виртуальность.
    struct CallProxy
    {
        typedef void (CallProxy::*CallFn)(WndMsg*, Wnd*);
        CallProxy(CallFn f)
            :callFn_(f)
        {}
        void Call(WndMsg* msg, Wnd* wnd)
        {
            (this->*callFn_)(msg, wnd);
        }
    private:
        CallFn callFn_;
    };
    template<UINT id, class T>
    struct CallProxyT
        :CallProxy
    {
        typedef void (T::*MfnType)(Msg<id>&);
        CallProxyT(MfnType fn)
            :CallProxy(static_cast<CallProxy::CallFn>(&CallProxyT::Call))
            ,fn_(fn)
        {}
        void Call(WndMsg* msg, Wnd* wnd)
        {
            (static_cast<T*>(wnd)->*fn_)(Msg<id>(msg));
        }
    private:
        MfnType fn_;
    };
    template<UINT id, class T>
    static CallProxy& StaticCallProxy(void (T::*fn)(Msg<id>&))
    {
        static CallProxyT<id, T> proxy(fn);
        return proxy;
    }
    CallProxy& proxy_;
public:
    const UINT id_;
    template<UINT id, class T>
    Handler(void (T::*fn)(Msg<id>&))
        :id_(id)
        ,proxy_(StaticCallProxy(fn))
    {}
... << RSDN@Home 1.0 beta 6a >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[2]: По мотивам "Прощания Славянки"
От: limax Эстония http://mem.ee
Дата: 09.05.03 10:19
Оценка: 6 (1)
Здравствуйте, c-smile, Вы писали:
CS>Чего бы просто не написать:
CS>  virtual bool on_create(...){...}
CS>  virtual bool on_die(...){...}
CS>  virtual bool on_mouse(...){...}
CS>  virtual bool on_key(...){...}
CS>  virtual bool on_focus(...){...}
CS>  virtual bool on_size(gool::size oldsize) {...}


CS> И гори оно огнем....


Т.е. ты предлагаешь пойти по методу WinLib от Бартоша Милефски?
Я таким и пользовался года два. Перечислю недостатки, в порядке возрастания вредности:

  1. Стандартных сообщений много, каждое хочется прописать в библиотеку. Получаем опупенную vtable для каждого класса. Согласен, это мелочь, но неприятно.
  2. Все сообщения обрабатывающиеся таким образом должны быть прописаны ЧАСТНЫМ образом в ProcessMessage или на худой конец в WndProc (если ты конечно используешь параметры специфичные для каждого сообщения, а не тупые WPARAM/LPARAM). При расширении библиотеки нужно следить за добавлением обработчиков.
  3. Ошибка в одной букве в имени, или неправильный порядок аргументов при переопределении обработчика — и прощай надёжность. Каждый раз нужно заглядывать в библиотечное определение и проверять, так ли ты написал.
  4. Любое изменение определения обработчика в библиотеке сопровождается геммороем поиска всех обработчиков в клиентском коде. Т.е. "написал — не вздумай больше трогать". Монолитность старого кода — очень хреново. Именно это являлось для меня самым большим минусом , т.к. я сильно дорабатывал библиотеку в течении всего времени эксплуатации.
CS>Или в извращениях есть некая прэлэсть которой я не вижу?
Основные плюсы по сравнению с WinLib:

  1. Гибкость и независимость от библиотеки. При добавлении обработчика нового сообщения, все специфичные для сообщения вещи указываются в специализации сообщения, т.е. шаблона аргумента обработчика (те самые Msg<WM_PAINT>).
  2. Стандарт записи обработчика в клиентском коде. Не нужно искать и записывать типы и порядок аргументов обработчика. все специфичные для сообщения параметры и методы находятся в одном аргументе.
  3. Локальность, а в варианте от lboss
    Автор: lboss
    Дата: 08.05.03
    (правда над ним ещё подумать надо: магия в коде — это не всегда безопасно ) и атомарность определений обработчиков для конкретного класса.
Have fun: Win+M, Ctrl+A, Enter
Re[3]: По мотивам SWL
От: limax Эстония http://mem.ee
Дата: 09.05.03 10:47
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Хм я и не знал что столько трюков использовал

Ну дык... Забыл, как сам учился С++ и хлопал себя по лбу, увидев очередную полезную вещь реализованную в одной строке?

L>Единственное место, где указатель лучше оставить — это GetHandler, но там я сделал следующую конструкцию, чтобы указатель можно было только использовать для доступа к членам:

WH>Хм Не вижу смысла.
Смысл в том, чтобы сделать библиотеку доступной бестолковому программисту. Т.е. нужна некоторая защита от дурака. Получить "ссылку" и проверить её валидность можно, а вот использовать объект, кроме как методами — нельзя. Сделал просто так, "для кумплекту"

L>  T* _ref; //WH> Тут _ref дальше _ptr //L> угму. по памяти набивал, да и ссылка там сначала была.


WH>Это мелочи... Но имена _* и __* насколько я помню зарезервированы для разработчиков компилятора.

Старая песня:
  1. Имена __* зарезирвированы.
  2. Имена _X*, где X — ЗАГЛАВНАЯ буква, зарезервированы. Но _abcd — можно использовать в клиентском коде.
По-моему так.
А если даже кое-где и не так, то фиг с ним. Мне намного удобнее писать (и нагляднее читать) _member, чем member_ или не дай бог m_member.
Have fun: Win+M, Ctrl+A, Enter
Re[3]: По мотивам "Прощания Славянки"
От: c-smile Канада http://terrainformatica.com
Дата: 09.05.03 15:58
Оценка: +1
Здравствуйте, limax, Вы писали:

L>... Перечислю недостатки, в порядке возрастания вредности:


L>

    L>
  1. Стандартных сообщений много, каждое хочется прописать в библиотеку. Получаем опупенную vtable для каждого класса. Согласен, это мелочь, но неприятно.

    Первое. Стандартных сообщений не много. десяток. ну может два.
    Первичных событий вообще столько же сколько методов ввода — два : клавиатура, мышь.
    Плюс события оконной системы.

    Если их свести в группы типа: получится вообще

    class widget {
    virtual bool on(paint_event& evt);
    virtual bool on(mouse_event& evt);
    virtual bool on(key_event& evt);
    virtual bool on(window_event& evt);
    virtual bool on(command_event& evt);
    }


    Про vtable: а предлагамые map<event> или if(msg == 1) {} else if (msg == 2) {} (в стиле WTL) будут меньше места занимать и быстрее выполняться?

    По этому пункту не убедил.


    L>
  2. Все сообщения обрабатывающиеся таким образом должны быть прописаны ЧАСТНЫМ образом в ProcessMessage или на худой конец в WndProc (если ты конечно используешь параметры специфичные для каждого сообщения, а не тупые WPARAM/LPARAM). При расширении библиотеки нужно следить за добавлением обработчиков.

    Тут я не понял совсем... Опять в плену мифа о множественности сообщений?
    Ты не путай:

    есть класс widget — это абстрактное child window.
    и есть класс widget_behavior — драйвер специфических сообщений конкретного типа контрола. Это разные сущности.

    L>
  3. Ошибка в одной букве в имени, или неправильный порядок аргументов при переопределении обработчика — и прощай надёжность. Каждый раз нужно заглядывать в библиотечное определение и проверять, так ли ты написал.

    Не понял. Ты про что? Ты вообще С++ имеешь ввиду, кстати?

    L>
  4. Любое изменение определения обработчика в библиотеке сопровождается геммороем поиска всех обработчиков в клиентском коде. Т.е. "написал — не вздумай больше трогать". Монолитность старого кода — очень хреново. Именно это являлось для меня самым большим минусом , т.к. я сильно дорабатывал библиотеку в течении всего времени эксплуатации.

    А это ошибка проектирования и ни что другое.
    Никакие templates и пр. эту проблему не решают.

    L>
CS>Или в извращениях есть некая прэлэсть которой я не вижу?
L>Основные плюсы по сравнению с WinLib:

L>

    L>
  1. Гибкость и независимость от библиотеки. При добавлении обработчика нового сообщения, все специфичные для сообщения вещи указываются в специализации сообщения, т.е. шаблона аргумента обработчика (те самые Msg<WM_PAINT>).
    L>
  2. Стандарт записи обработчика в клиентском коде. Не нужно искать и записывать типы и порядок аргументов обработчика. все специфичные для сообщения параметры и методы находятся в одном аргументе.
    L>
  3. Локальность, а в варианте от lboss
    Автор: lboss
    Дата: 08.05.03
    (правда над ним ещё подумать надо: магия в коде — это не всегда безопасно ) и атомарность определений обработчиков для конкретного класса.
    L>

Я уже лет шесть по разными GUI подходами занимаюсь...
И вот в результате вернулся к истокам: http://terra-informatica.org/j-smile
Получил массу удовольствия имплементируя.
Re[4]: По мотивам "Прощания Славянки"
От: WolfHound  
Дата: 09.05.03 17:27
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Первое. Стандартных сообщений не много. десяток. ну может два.

В WTL поддержка 171ого сообщения
CS>По этому пункту не убедил.
А я?

CS>Тут я не понял совсем... Опять в плену мифа о множественности сообщений?

CS>Ты не путай:
Хм... имелось в виду что в твоей системе придеться в цикле обработки сообщений прописать все сообщения которые ты будеши обрабатывать. А также придется создать механизм обработки не стандартных сообщений. В данной системе все делается едино образно и в цикле обработки сообщений придется только создать обьект WndMsg что делается до смешного тривиально.

CS>есть класс widget — это абстрактное child window.

CS>и есть класс widget_behavior — драйвер специфических сообщений конкретного типа контрола. Это разные сущности.
Зачем?

CS>Не понял. Ты про что? Ты вообще С++ имеешь ввиду, кстати?

А теперь посмотри что ты написал в начале...

CS>А это ошибка проектирования и ни что другое.

Это рядовая ошибка кодирования
CS>Никакие templates и пр. эту проблему не решают.
Еще как решают.

CS>Я уже лет шесть по разными GUI подходами занимаюсь...

CS>И вот в результате вернулся к истокам: http://terra-informatica.org/j-smile
CS>Получил массу удовольствия имплементируя.
Давай по существу ладно.
... << RSDN@Home 1.0 beta 6a >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[5]: И ты тоже не убедил....
От: c-smile Канада http://terrainformatica.com
Дата: 10.05.03 01:08
Оценка: 15 (1)
Здравствуйте, WolfHound, Вы писали:




WH>Здравствуйте, c-smile, Вы писали:


WH>В WTL поддержка 171ого сообщения


Про WTL не надо. Это партизанская либа для использования в глыбоком тылу врага.

Под поддержкой ты имеешь ввиду конструкции вида:
MESSAGE_HANDLER(WM_CREATE, OnCreate); Или все ж message crackers?

Или что-то типа SB_SETTEXT? Дык это не сообщение а имя функции.

Java AWT (сакс, но тем не менее) обходится 8 (+/- 2) events. И это правильно.
В том смысле что код обозримый получается.

(дальше поскипано т.к. не по существу :-P )

Давай определимся с терминами:

class window — то что в Win32 называется topmost window.
class widget — то что называется child window.

Очевидно что эти два класса имеют совершенно разные системы событий, например:
window::on(event_activate& evt); и widget::on(event_keyboard& evt);

Понятно что window и widget это абстрактные классы в том смысле что они описывают все окна и все контролы вообще.

События.

Давай договоримся что WM_*BUTTONDOWN, WM_MOUSEMOVE, etc. (всего мышиных кодов 15 шт) это одно событие. В смысле таксономии и общности обработки.

Т.е. вполне возможно (и удобно!) написать widget::on(event_mouse& evt).

Все остальные WM_* события тоже укладываются в такие компактные группы.

Радикально другая ситуация с нотификационными событиями типа:
WM_COMMAND и WM_NOTIFY.

Для их обработки имеет смысл организовывать нечто типа списка listeners (списка функций) в Java.
Потому как ну не станшь же писать свой класс для получения on_some_button_press.


ну и написать обертки типа list_box: public widget дело техники.


Типа того.
Re[3]: Можно сильнее извратиться....
От: lboss Россия  
Дата: 12.05.03 05:54
Оценка:
Здравствуйте, limax, Вы писали:

L>Здравствуйте, lboss, Вы писали:

L>Но я не могу придумать как форсировать вызов конструктора Initializer'а... Пришлось придумывать MsgResult...
L>Интересная методика. Осталось разобраться почему она вообще работает.
L>...
L>Ах-ха! Нашёл.
L>
L>template <class TRel, UINT msgID>
L> typename MsgResult<TRel, msgID>::Initializer
L>  MsgResult<TRel, msgID>::m_Initializer;
L>

L>А будет оно будет работать с разделением исходника на файлы?
L>Куда instance этого самого статического члена шаблона ставить?

??? Это ты про что?! Это просто метот генерации статического шаблонного поля. "с разделением исходника на файлы" — это вообще не связанно... Это аналогично статической функции внутри класса — просто генерится перекрывающийся сегмент — линкер оставляет только один из них...
С уважением Вадим.
Re[2]: По мотивам "Прощания Славянки"
От: lboss Россия  
Дата: 12.05.03 06:08
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Здравствуйте, ГоночныйВолк со товарищи!


CS>А вот у меня вопрос возник:


CS>На кой ляд все эти танцы с бубнами?


CS>
CS>class window() 
CS>{

CS>// primordial events

CS>  virtual bool on_create(...){...}
CS>  virtual bool on_die(...){...}
CS>  virtual bool on_mouse(...){...}
CS>  virtual bool on_key(...){...}
CS>  virtual bool on_focus(...){...}
CS>  virtual bool on_size(gool::size oldsize) {...}
CS>...

CS>// synthesized events
CS>  virtual bool on_command(int code, ...) {...}
CS>  virtual bool on_notify(int code, ...) {...}

CS>  virtual bool on_unknown(uint msg, wparam, lparam...)  

CS>};
CS>


Тогда уж лучше так:


template <class TRel>
class window
{
public:
  bool on_create(Msg<WM_CREATE> & ){ return false; }
  bool on_focus(Msg<WM_SETFOCUS> & ){ return false; }
  bool on_size(Msg<WM_SIZE> & ) { return false; }
...

  virtual bool onEvent(UINT msgID, MsgData & data)
  {
    switch(msgID)
    {
    case WM_CREATE:
      return static_cast<TRel *>(this)->on_create(Msg<WM_CREATE>(data));
    case WM_SETFOCUS:
      return static_cast<TRel *>(this)->on_focus(Msg<WM_SETFOCUS>(data));
    case WM_SIZE:
      return static_cast<TRel *>(this)->on_size(Msg<WM_SIZE>(data));
...
    }
    return false;
  }
};



Тут оба подхода — и таблица виртуальных функций не раздувается...
С уважением Вадим.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.