По мотивам 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;
  }
};



Тут оба подхода — и таблица виртуальных функций не раздувается...
С уважением Вадим.
Re[3]: По мотивам "Прощания Славянки"
От: Kluev  
Дата: 12.05.03 08:20
Оценка:
Здравствуйте, lboss, Вы писали:

L>Здравствуйте, 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>


А, я понял. c-smile на java пишетему не до шаблонов
Re[4]: Уточнение насчёт имён
От: limax Эстония http://mem.ee
Дата: 12.05.03 09:35
Оценка:
WH>Это мелочи... Но имена _* и __* насколько я помню зарезервированы для разработчиков компилятора.
В стандарт смотреть не хочу, т.к. его всё равно у меня нет , но вот из Deep C++ вписанной в MSDN:

The rules differ slightly between the two languages. In C90 and C99, the implementation reserves

In C++, the implementation reserves

Have fun: Win+M, Ctrl+A, Enter
Re[4]: По мотивам "Прощания Славянки"
От: limax Эстония http://mem.ee
Дата: 12.05.03 11:04
Оценка:
Здравствуйте, c-smile, Вы писали:

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

Во-первых, я ничего не знаю про твою реализацию, поэтому в предыдущем сообщении говорил про реализацию WinLib с relisoft.com, а там сообщений много.
Но это не важно, т.к. я уже сказал — vtable — это мелочь. В шаблонных обработчиках статические инициализаторы карт сообщений тоже немало места скушают, однако здесь размер зависит только от количества обрабатываемых классом сообщений.

L>
  • Все сообщения должны быть прописаны ЧАСТНЫМ образом в ProcessMessage.
    CS>Тут я не понял совсем... Опять в плену мифа о множественности сообщений?

    Все сообщения (не важно, много или мало) нужно взламывать в библиотеке и вызывать конкретные обработчики или, как у тебя, обработчики категорий. Т.е. вся обработка параметров сообщений (т.е. взлом сообщений) намертво зашита в библиотеку. Мне, лично, это не нравится.
    А в шаблонном варианте взломщик можно специалировать где угодно и как угодно. Можно даже специализировать по классу окон (например MsgSpec<DummyWindow,WM_PAINT>:Msg<WM_PAINT>), чтобы иметь возможность добавлять (или наоборот убирать) что-то совсем специфическое для конкретного класса, но не в обработчик, а во взломщик.

    L>
  • Ошибка в одной букве в имени, или неправильный порядок аргументов при переопределении обработчика — и прощай надёжность. Каждый раз нужно заглядывать в библиотечное определение и проверять, так ли ты написал.
    CS>Не понял. Ты про что? Ты вообще С++ имеешь ввиду, кстати?
    Конечно С++, что же ещё? Или у тебя редактор умеет подсвечивать виртуальные функции, не входящие в базовый класс? (Хотя можно посмотреть в дереве, но всё равно искать). Компилятор не может тебя предупредить, что ты определил новую виртуальную функцию on_mmove(ButtonState,KeyState) или wm_mousemove(KeyState,ButtonState) вместо переопределения wm_mousemove(ButtonState,KeyState). Я именно это имел в виду. Хотя в твоём варианте с on(mouse_event&) эта проблема вроде исчезает, однако, по-моему, появляется другая: при добавлении в дочернем классе одной новой функции on(death_for_all&) компилятор прячет все старые on(...).


    L>
  • Монолитность старого кода — очень хреново.
    CS>А это ошибка проектирования и ни что другое.
    CS>Никакие templates и пр. эту проблему не решают.
    Любой монолитный код, каким бы удобным и хорошо спроектированным он не был по-началу, будет когда-нибудь нуждаться в переписывании. Монолитность — bad, это у некоторых уже в определениях, в том числе и у меня.

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


    И хорошо.
    А вообще, подводя итог:
    Давай закроем тему? Это бесполезный спор. Каждый останется при своём мнении и будет пользовать тем, что ему удобнее. Тем более, что я не хочу яростно защищать ни один из существующих методов обработки сообщений — у каждого своих недостатков предостаточно.

    Если есть удобный рабочий вариант с виртуальными обработчиками категорий сообщений, то запости в Исходники. Он будет для кого-нибудь действительно полезнее шаблонных обработчиков.
  • Have fun: Win+M, Ctrl+A, Enter
    Re[6]: И ты тоже не убедил....
    От: limax Эстония http://mem.ee
    Дата: 12.05.03 11:15
    Оценка:
    Здравствуйте, c-smile, Вы писали:
    CS>Про WTL не надо. Это партизанская либа для использования в глыбоком тылу врага.
    У моего друга на работе основные библиотеки ATL и WTL. Очень крупная фирма. Чего ж тут партизанского?

    CS>Под поддержкой ты имеешь ввиду конструкции вида:

    CS>MESSAGE_HANDLER(WM_CREATE, OnCreate);
    Это вообще гадость — в каждом обработчике все параметры вручную обрабатывать.

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

    CS>class widget — то что называется child window.
    CS>Очевидно что эти два класса имеют совершенно разные системы событий, например:
    CS>window::on(event_activate& evt); и widget::on(event_keyboard& evt);
    Кхм. А если у меня дочернее окно с заголовком, то это куда? Как в WinRAR окно рас/запаковки, или как почти все docking windows.
    Have fun: Win+M, Ctrl+A, Enter
    Re[4]: Можно сильнее извратиться....
    От: limax Эстония http://mem.ee
    Дата: 12.05.03 11:27
    Оценка:
    Здравствуйте, lboss, Вы писали:

    L>"с разделением исходника на файлы" — это вообще не связанно... Это аналогично статической функции внутри класса — просто генерится перекрывающийся сегмент — линкер оставляет только один из них...


    Угу, проверил — работает. Просто у меня в памяти остались глюки с такими трюками в старых компиляторах.

    Кстати, совсем забыл указать на явную ошибку приводящую к большому leak-у в каждой карте сообщений.
    std::map::operator[] предназначен не для доступа, а для создания новой пары. Он создаёт новую пару с заданным ключом, и возвращает ссылку на второй элемент в паре. Константного метода не сделали специально, чтобы не было путаницы.

    #if BAD
        ProcessMessageFunc pf = m_map[msg.m_msg]; //большая утечка
        if(pf)
            return pf(wnd, msg);
        else
            return false;
    #else
    //Исправление:
        MapIterConst it = m_map.find(msg.m_msg);
        if(it!=m_map.end())
            return it->second(wnd, msg);
        else
            return false;
    #endif
    Have fun: Win+M, Ctrl+A, Enter
    Re[4]: По мотивам "Прощания Славянки"
    От: limax Эстония http://mem.ee
    Дата: 12.05.03 12:16
    Оценка:
    Здравствуйте, c-smile, Вы писали:
    CS>Я уже лет шесть по разными GUI подходами занимаюсь...
    CS>И вот в результате вернулся к истокам: http://terra-informatica.org/j-smile
    CS>Получил массу удовольствия имплементируя.
    Кстати, удовольствие удовольствием, но j-smile demo не закрывает процесс при закрытии окна — приходится его вручную убивать. Смотрел на Win2k.
    А контролы — приятные, ничего не скажешь.
    Have fun: Win+M, Ctrl+A, Enter
    Re[5]: Можно сильнее извратиться....
    От: lboss Россия  
    Дата: 12.05.03 13:05
    Оценка:
    Здравствуйте, limax, Вы писали:


    L>Кстати, совсем забыл указать на явную ошибку приводящую к большому leak-у в каждой карте сообщений.

    L>std::map::operator[] предназначен не для доступа, а для создания новой пары. Он создаёт новую пару с заданным ключом, и возвращает ссылку на второй элемент в паре. Константного метода не сделали специально, чтобы не было путаницы.

    Боюсь и тут ты не прав — да создается новая пара — но(!) она вносится в список — и соответственно во второй раз она и возвращается (и соответственно разрушается в деструкторе) — ни какой утечки... В моём коде плюс — не вызваются лишние конструкторы итераторов, минус — замедляется поиск — так что и так и так правильно — но я подумал что с operator [] будет понятней...
    С уважением Вадим.
    Re[6]: Можно сильнее извратиться....
    От: limax Эстония http://mem.ee
    Дата: 12.05.03 13:47
    Оценка:
    Здравствуйте, lboss, Вы писали:

    L>Боюсь и тут ты не прав — да создается новая пара — но(!) она вносится в список — и соответственно во второй раз она и возвращается (и соответственно разрушается в деструкторе) — ни какой утечки... В моём коде плюс — не вызваются лишние конструкторы итераторов, минус — замедляется поиск — так что и так и так правильно — но я подумал что с operator [] будет понятней...


    "Не надо ля-ля"
    Такую интереснейшую схему неявных карт сообщений привёл, что никому и в голову не приходила, а на таких простых вещах путаешься.
    1. в карте сообщений нужно столько записей, сколько есть обработчиков сообщений. Если больше — уже утечка. Ну и что, что она потом "возвращается". Если тебя не устраивает термин "утечка", можно использовать "чрезмерное использование ресурсов" — всё равно плохо.

    2. сообщения окну могут слаться самые разные — никто не гарантирует, что только 200 стандартных. В частности, любой может зарегистрировать именованое сообщение в системе и использовать его для опознавания "свой-чужой" (если не больше). А попробовать цикл с посылкой сообщений от 1 до 1000000000 ты пробовал? Вылет гарантирован.

      Кстати, std::map жрёт память намного быстрее Loki::AssocVector-а из-за не-in-place хранения объектов и дополнительных расходов на указатели.

    3. какие ещё "лишние конструкторы итераторов"??? Сравни в асме _map.find() и _map[]. Все эти "лишние конструкторы итераторов" есть и в варианте с "доступом" через _map[], только с довеском в виде создания ненужных пар. К тому же, ты сам говоришь, что "замедляется поиск". А для чего доступ-то требуется? Только для поиска обработчика.
    Have fun: Win+M, Ctrl+A, Enter
    Re[4]: По мотивам "Прощания Славянки"
    От: c-smile Канада http://terrainformatica.com
    Дата: 12.05.03 15:32
    Оценка:
    Здравствуйте, Kluev, Вы писали:

    K>А, я понял. c-smile на java пишетему не до шаблонов


    На всем что шевелится ....
    (http://terra-informatica.org/gool/gl_geometry.h.htm)

    GUI j-smile написан на java по образцу моей же c++ либы....

    
    namespace gool {
    
    class widget 
    {
    //|
    //| widget - generic visible object
    //|
      friend class widgets;
      friend class window;
      friend class widget_behavior;
      friend class widget_style;
    
      public:
      protected:
        rect                _place;
        widgets*            _parent;
        widget_behavior*    _behavior;
        cursor_t            _cursor_type; 
      public:
        widget();
        widget(const widget& w);
        widget(rect &place);
    
        virtual ~widget();
    
    
        virtual window*       root();
        virtual widgets*      parent()                    { return _parent; };
        virtual widget*       find(point pt)              { return contains(pt)? this: 0; }; 
    
        virtual void          set_capture(bool onOff);    
        virtual void          cursor(cursor_t num);
        virtual cursor_t      cursor();
    
        virtual const rect&   place()                     { return _place; };
        virtual rect          client_place();              
    
        virtual int           baseline(); // if 0 - not set
    
        virtual point         offset()                    { return _offset; }
        virtual void          offset(point of)            { _offset = of; }
    
        virtual rect          place_on_window();
        virtual rect          place_on_screen();
       
        virtual int           desired_width();
        virtual int           desired_height(int width);
        
        virtual void          invalidate(rect& r);   
        virtual void          invalidate()                { invalidate(rect(0,0,_place.width()-1,_place.height()-1)); };
    
        virtual void          update();
    
        virtual void          move(const rect& r);         
        virtual void          move(const point p)         { move(rect(p,_place.size())); };
        virtual void          move(const point p,size s)  { move(rect(p,s)); };
    
        // pt in parent coord system
        virtual bool          contains(point pt)          { return _place && pt; };
        virtual bool          contains(rect rc)           { return _place && rc; };
    
        virtual bool          draw(surface& sf);
    
        virtual bool          on(event_mouse &evt);
        virtual bool          on(event_key &evt)  ;
        virtual bool          on(event_focus &evt);
        virtual bool          on(event_timer &evt);
    
        virtual bool          set_focus();
        virtual bool          has_focus();
    
        virtual widget_behavior * 
                              behavior()                  { return _behavior; }
        virtual void          behavior(widget_behavior * wb);
    
        dword                 start_timer(dword ms);
        void                  stop_timer(dword id);
    
    };
    
    }
    Re[5]: По мотивам "Прощания Славянки"
    От: c-smile Канада http://terrainformatica.com
    Дата: 12.05.03 15:45
    Оценка: :)
    Здравствуйте, limax, Вы писали:

    L>Кстати, удовольствие удовольствием, но j-smile demo не закрывает процесс при закрытии окна — приходится его вручную убивать. Смотрел на Win2k.


    Бага известная. Концепт кары вообще как правило мотора не имеют .
    Re[7]: Можно сильнее извратиться....
    От: lboss Россия  
    Дата: 13.05.03 08:08
    Оценка:
    Здравствуйте, limax, Вы писали:

    L>"Не надо ля-ля"

    L>Такую интереснейшую схему неявных карт сообщений привёл, что никому и в голову не приходила, а на таких простых вещах путаешься.

    За похвалу спасибо. За критику тоже, но:

    L>

      L>
    1. в карте сообщений нужно столько записей, сколько есть обработчиков сообщений. Если больше — уже утечка. Ну и что, что она потом "возвращается". Если тебя не устраивает термин "утечка", можно использовать "чрезмерное использование ресурсов" — всё равно плохо.


      Утечка (Leak) — это здесь — оновное это то что оно рушит программу — так как ресурс расходуется не возвратно...

      В данном случае — это не leak!!! Но в любом случае — повторю — я привёл реализацию для более понятного кода...


      L>
    2. сообщения окну могут слаться самые разные — никто не гарантирует, что только 200 стандартных. В частности, любой может зарегистрировать именованое сообщение в системе и использовать его для опознавания "свой-чужой" (если не больше). А попробовать цикл с посылкой сообщений от 1 до 1000000000 ты пробовал? Вылет гарантирован.

      Ох... Запусти и глянь — думаю ты очень удивишься...
      Код сгенерён Wizard'ом — я добавил 3 строчки (затаганы <Vadim>):

      // Test1.cpp : Defines the entry point for the application.
      //
      
      #include "stdafx.h"
      #define WIN32_LEAN_AND_MEAN        // Exclude rarely-used stuff from Windows headers
      
      
      // Windows Header Files:
      #include <windows.h>
      
      // C RunTime Header Files
      #include <stdlib.h>
      #include <malloc.h>
      #include <memory.h>
      #include <tchar.h>
      
      #include <map>
      
      #include "resource.h"
      
      #define MAX_LOADSTRING 100
      
      // Global Variables:
      HINSTANCE hInst;                                // current instance
      TCHAR szTitle[MAX_LOADSTRING];                                // The title bar text
      TCHAR szWindowClass[MAX_LOADSTRING];                                // The title bar text
      
      // Foward declarations of functions included in this code module:
      ATOM                MyRegisterClass(HINSTANCE hInstance);
      BOOL                InitInstance(HINSTANCE, int);
      LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
      LRESULT CALLBACK    About(HWND, UINT, WPARAM, LPARAM);
      
      int APIENTRY WinMain(HINSTANCE hInstance,
                           HINSTANCE hPrevInstance,
                           LPSTR     lpCmdLine,
                           int       nCmdShow)
      {
           // TODO: Place code here.
          MSG msg;
          HACCEL hAccelTable;
      
          // Initialize global strings
          LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
          LoadString(hInstance, IDC_TEST1, szWindowClass, MAX_LOADSTRING);
          MyRegisterClass(hInstance);
      
          // Perform application initialization:
          if (!InitInstance (hInstance, nCmdShow)) 
          {
              return FALSE;
          }
      
          hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_TEST1);
      
          // Main message loop:
          while (GetMessage(&msg, NULL, 0, 0)) 
          {
              if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 
              {
                  TranslateMessage(&msg);
                  DispatchMessage(&msg);
              }
          }
      
          return msg.wParam;
      }
      
      
      
      //
      //  FUNCTION: MyRegisterClass()
      //
      //  PURPOSE: Registers the window class.
      //
      //  COMMENTS:
      //
      //    This function and its usage is only necessary if you want this code
      //    to be compatible with Win32 systems prior to the 'RegisterClassEx'
      //    function that was added to Windows 95. It is important to call this function
      //    so that the application will get 'well formed' small icons associated
      //    with it.
      //
      ATOM MyRegisterClass(HINSTANCE hInstance)
      {
          WNDCLASSEX wcex;
      
          wcex.cbSize = sizeof(WNDCLASSEX); 
      
          wcex.style            = CS_HREDRAW | CS_VREDRAW;
          wcex.lpfnWndProc    = (WNDPROC)WndProc;
          wcex.cbClsExtra        = 0;
          wcex.cbWndExtra        = 0;
          wcex.hInstance        = hInstance;
          wcex.hIcon            = LoadIcon(hInstance, (LPCTSTR)IDI_TEST1);
          wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
          wcex.hbrBackground    = (HBRUSH)(COLOR_WINDOW+1);
          wcex.lpszMenuName    = (LPCSTR)IDC_TEST1;
          wcex.lpszClassName    = szWindowClass;
          wcex.hIconSm        = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);
      
          return RegisterClassEx(&wcex);
      }
      
      //
      //   FUNCTION: InitInstance(HANDLE, int)
      //
      //   PURPOSE: Saves instance handle and creates main window
      //
      //   COMMENTS:
      //
      //        In this function, we save the instance handle in a global variable and
      //        create and display the main program window.
      //
      BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
      {
         HWND hWnd;
      
         hInst = hInstance; // Store instance handle in our global variable
      
         hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
      
         if (!hWnd)
         {
            return FALSE;
         }
      
         ShowWindow(hWnd, nCmdShow);
         UpdateWindow(hWnd);
      
         return TRUE;
      }
      
      //
      //  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
      //
      //  PURPOSE:  Processes messages for the main window.
      //
      //  WM_COMMAND    - process the application menu
      //  WM_PAINT    - Paint the main window
      //  WM_DESTROY    - post a quit message and return
      //
      //
      LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
      {
          int wmId, wmEvent;
          PAINTSTRUCT ps;
          HDC hdc;
          TCHAR szHello[MAX_LOADSTRING];
      
      //<Vadim>
          static    std::map<UINT, bool>    allMsg;
          allMsg[message] = true;
          _stprintf(szHello, _T("%u"), allMsg.size());
      //</Vadim>
      
          switch (message) 
          {
              case WM_COMMAND:
                  wmId    = LOWORD(wParam); 
                  wmEvent = HIWORD(wParam); 
                  // Parse the menu selections:
                  switch (wmId)
                  {
                      case IDM_ABOUT:
                         DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
                         break;
                      case IDM_EXIT:
                         DestroyWindow(hWnd);
                         break;
                      default:
                         return DefWindowProc(hWnd, message, wParam, lParam);
                  }
                  break;
              case WM_PAINT:
                  hdc = BeginPaint(hWnd, &ps);
                  // TODO: Add any drawing code here...
                  RECT rt;
                  GetClientRect(hWnd, &rt);
                  DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);
                  EndPaint(hWnd, &ps);
                  break;
              case WM_DESTROY:
                  PostQuitMessage(0);
                  break;
              default:
                  return DefWindowProc(hWnd, message, wParam, lParam);
         }
         return 0;
      }
      
      // Mesage handler for about box.
      LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
      {
          switch (message)
          {
              case WM_INITDIALOG:
                      return TRUE;
      
              case WM_COMMAND:
                  if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) 
                  {
                      EndDialog(hDlg, LOWORD(wParam));
                      return TRUE;
                  }
                  break;
          }
          return FALSE;
      }



      L>Кстати, std::map жрёт память намного быстрее Loki::AssocVector-а из-за не-in-place хранения объектов и дополнительных расходов на указатели.


      В данном случае это не очень важно...

      L>
    3. какие ещё "лишние конструкторы итераторов"??? Сравни в асме _map.find() и _map[]. Все эти "лишние конструкторы итераторов" есть и в варианте с "доступом" через _map[], только с довеском в виде создания ненужных пар. К тому же, ты сам говоришь, что "замедляется поиск". А для чего доступ-то требуется? Только для поиска обработчика.
      L>
      Это ты привязался к реализации... Map в быстрой реализации работает без итераторов... а find() и end() конструируют и разрушают итераторы (и тоже проводят поиск как и operator[])... Другое дело, что это хорошо оптимизится... Но опять говорю — ты прав — в реальности там проще hash table поюзать... Опять таки в стандартный STL она не входит — поэтому я для наглядности map привел...
    С уважением Вадим.
    Re[8]: Можно сильнее извратиться....
    От: limax Эстония http://mem.ee
    Дата: 13.05.03 08:58
    Оценка:
    Здравствуйте, lboss, Вы писали:

    L>Утечка (Leak) — это здесь — оновное это то что оно рушит программу — так как ресурс расходуется не возвратно...

    L>В данном случае — это не leak!!! Но в любом случае — повторю — я привёл реализацию для более понятного кода...
    Давай разбираться.
    1. Любая утёкшая память освобождает операционной системой при закрытии программы. До закрытия, утечки могут (и вероятно будут) только накапливаться.
    2. Память выделенная статической карте сообщений будет освобождена только при закрытии программы. До закрытия, размер карты может (и вероятно будет) только увеличиваться.
    Я не вижу разницы между первым и вторым для пользователя, так какой толк в определении утечек?
    Поэтому считаю такие расходы неприемлимыми.

    L>Ох... Запусти и глянь — думаю ты очень удивишься...

    Ничего удивительного. В большинстве случаев расширения карты будут незначительными, но никто не гарантирует, что они такими и останутся в каждом случае — потенциально карта может расти до объёма всей памяти. Я уже привёл пример новых сообщений. Так зачем писать недетерминированный код?

    L>Это ты привязался к реализации... Map в быстрой реализации работает без итераторов... а find() и end() конструируют и разрушают итераторы (и тоже проводят поиск как и operator[])... Другое дело, что это хорошо оптимизится...

    Не совсем понял смысл. В какой это быстрой реализации map работает без итераторов?
    Вот выдержка из реализация std::map в VC7.1
        mapped_type& operator[](const key_type& _Keyval)
            {    // find element matching _Keyval or insert with default mapped
            iterator _Where = this->lower_bound(_Keyval);
            if (_Where == this->end() || this->comp(_Keyval, this->_Key(_Where._Mynode())))
                _Where = this->insert(_Where,
                    value_type(_Keyval, mapped_type()));
            return ((*_Where).second);
            }
        iterator find(const key_type& _Keyval)
            {    // find an element in mutable sequence that matches _Keyval
            iterator _Where = lower_bound(_Keyval);
            return (_Where == end() || this->comp(_Keyval, _Key(_Where._Mynode()))
                ? end() : _Where);
            }

    И там, и там, используется итератор+lower_bound
    Для сравнения, Loki::AssocVector — аналогично:
        // 23.3.1.2 element access:
        mapped_type& operator[](const key_type& key)
            { return insert(value_type(key, mapped_type())).first->second; }
    
        // modifiers:
        std::pair<iterator, bool> insert(const value_type& val)
            {
            bool found(true);
            iterator i(lower_bound(val.first));
    
            if (i == end() || this->operator()(val.first, i->first))
                {
                i = Base::insert(i, val);
                found = false;
                }
            return std::make_pair(i, !found);
            }
    
        // 23.3.1.3 map operations:
        iterator find(const key_type& k)
            {
            iterator i(lower_bound(k));
            if (i != end() && this->operator()(k, i->first))
                {
                i = end();
                }
            return i;
            }


    L>Но опять говорю — ты прав — в реальности там проще hash table поюзать... Опять таки в стандартный STL она не входит — поэтому я для наглядности map привел...


    Когда это я про хеш говорил??
    Для карты сообщений, отсортированный массив или бинарное дерево подходит как нельзя лучше. Конструируется она один раз, а двоичный поиск гарантирует почти везде одинаковую скорость поиска. Что такое логарифм — так, пшик один. Для карты из 100 сообщений максимум 7 сравнений, для 1000 — 10, для миллиона — 20

    Кстати, у меня остался вопрос. В форуме С/С++ его WolfHound задал, но пока никто на него не ответил.
    Почему тип возвращаемого значения конструируется заранее, а тип параметров — нет?
    Т.е. в RetType<T> func (ParamType<T>) RetType сконструируется, а ParamType — нет.
    Это стандартное явление? Как ты на него вообще вышел?

    Я тут покумекал — если это стандарт, то открываются довольно богатые возможности использования шаблонов С++, т.к. частично компенсируется отсутствие статических конструкторов. Вот только жаль, что типы параметров не конструируются.
    Have fun: Win+M, Ctrl+A, Enter
    Re[9]: Можно сильнее извратиться....
    От: lboss Россия  
    Дата: 13.05.03 09:52
    Оценка:
    Здравствуйте, limax, Вы писали:

    L>

      L>
    1. Любая утёкшая память освобождает операционной системой при закрытии программы. До закрытия, утечки могут (и вероятно будут) только накапливаться.
      L>
    2. Память выделенная статической карте сообщений будет освобождена только при закрытии программы. До закрытия, размер карты может (и вероятно будет) только увеличиваться.
      L>
    Я не вижу разницы между первым и вторым для пользователя, так какой толк в определении утечек?

    L>Поэтому считаю такие расходы неприемлимыми.

    Нет — основное отличие leak от неоптимального использования памяти в том, что при leak — программа в конечном итоге съедает всю памать и грохается... Это разные задачи — leak — это серъёзный баг, а неоптимальность — просто завышенные требования к ресурсам...

    L><skip/> Так зачем писать недетерминированный код?


    Сто раз уже сказал — для наглядности... Я вообще свой hash (он не добавляет элементы если не надо (а-ля lookup в MFC)) юзаю и не жужу...

    L>Не совсем понял смысл. В какой это быстрой реализации map работает без итераторов?

    L>Вот выдержка из реализация std::map в VC7.1

    Разачарую тебя, в VC по моему (и не только по моему) самая поганая реализация STL из тех что я видел...

    Обычно пишется что-то вроде:

    SecondType & operator[] (const KeyType & key)
    {
       TreePair * p = m_root;
       <Поиск по дереву (целиком на указателях)>
       <если не найдено - то вставка>
       return p->second;
    }
    
    iterator find(const KeyType & key)
    {
       TreePair * p = m_root;
       <Поиск по дереву (целиком на указателях)>
       return iterator(p);
    }

    Итератор коструируется толкько в самом конце...
    В любом случае — это всё лирика...


    L>Но опять говорю — ты прав — в реальности там проще hash table поюзать... Опять таки в стандартный STL она не входит — поэтому я для наглядности map привел...


    L>Когда это я про хеш говорил??


    Ты прав в том, что это не оптимальный алгоритм...

    L>Для карты сообщений, отсортированный массив или бинарное дерево подходит как нельзя лучше. Конструируется она один раз, а двоичный поиск гарантирует почти везде одинаковую скорость поиска. Что такое логарифм — так, пшик один. Для карты из 100 сообщений максимум 7 сравнений, для 1000 — 10, для миллиона — 20


    Ох и зачем ты меня логаритмам учищь? Или это буря восторга?... Но всё равно, Hash лучше — тут довольно предсказуемые значения hash-функцию постоить можно классную...

    L>Кстати, у меня остался вопрос. В форуме С/С++ его WolfHound задал, но пока никто на него не ответил.

    L>Почему тип возвращаемого значения конструируется заранее, а тип параметров — нет?
    L>Т.е. в RetType<T> func (ParamType<T>) RetType сконструируется, а ParamType — нет.

    Что значит зарание? Ни чего там зарание не конструируется...
    А насчет WolfHound — пусть читает стандарт — шаблоны расскрываются по мере надобности и того что не надо генерить не генерится....

    L>Это стандартное явление? Как ты на него вообще вышел?


    Я 12 лет уже на C++ программаю

    L>Я тут покумекал — если это стандарт, то открываются довольно богатые возможности использования шаблонов С++, т.к. частично компенсируется отсутствие статических конструкторов. Вот только жаль, что типы параметров не конструируются.


    Конструируются... Просто в точке вызова — если эту функцию кто-то позовет то в той точке и будет развёрнут шаблон...
    С уважением Вадим.
    Re[10]: Можно сильнее извратиться....
    От: limax Эстония http://mem.ee
    Дата: 13.05.03 10:21
    Оценка:
    Здравствуйте, lboss, Вы писали:

    Пропускаю про утечки.
    L>Ох и зачем ты меня логаритмам учищь? Или это буря восторга?...
    А как же

    L>Но всё равно, Hash лучше — тут довольно предсказуемые значения hash-функцию постоить можно классную...

    А примерчик можно? И сколько может потребоваться времени на поиск в худшем случае? Если больше того самого логарифма, то меня уже не устраивает. Насколько больше потребуется памяти по сравнению с отсортированноым массивом-картой?
    У меня с хешами опыта маловато (если вообще можно сказать, что есть).

    Li>Почему тип возвращаемого значения конструируется заранее, а тип параметров — нет?

    Li>Т.е. в RetType<T> func (ParamType<T>) RetType сконструируется, а ParamType — нет.
    L>Что значит зарание? Ни чего там зарание не конструируется...
    L>шаблоны расскрываются по мере надобности и того что не надо генерить не генерится....
    Если так, почему твоя идея с картами сообщений вообще работает? Ведь все эти обработчики никто не вызывает, если не сконструировать типы их возвращяемых значений. Замкнутый круг.
    Have fun: Win+M, Ctrl+A, Enter
    Re[11]: Можно сильнее извратиться....
    От: lboss Россия  
    Дата: 13.05.03 10:33
    Оценка: 5 (1)
    Здравствуйте, limax, Вы писали:

    L>Но всё равно, Hash лучше — тут довольно предсказуемые значения hash-функцию постоить можно классную...

    L>А примерчик можно? И сколько может потребоваться времени на поиск в худшем случае? Если больше того самого логарифма, то меня уже не устраивает. Насколько больше потребуется памяти по сравнению с отсортированноым массивом-картой?
    L>У меня с хешами опыта маловато (если вообще можно сказать, что есть).

    Это лучше книжки почитать — но скажу сразу, что int значения самые удобные и хорошо изученные для hash.

    L>Если так, почему твоя идея с картами сообщений вообще работает? Ведь все эти обработчики никто не вызывает, если не сконструировать типы их возвращяемых значений. Замкнутый круг.


    смотри:
    class A
    {
      void f(Templ<int> a)
      {
      }
      Templ<int> f1()
      {
        return Templ<int>();
      }
    };


    Это приведет к:
      A::f(BYTE * this, BYTE a[])
      {
      }
      A::f1(BYTE * this, BYTE  *retType)
      {
        Templ<int>::Templ<int>(&retType);
      }


    Тоесть компилятор начнет для функции f1 инстанциировать конструктор темплейта, а он потянет за собой истанциирование статического мембера...
    С уважением Вадим.
    Re[10]: Можно сильнее извратиться....
    От: WFrag США  
    Дата: 13.05.03 10:53
    Оценка:
    Здравствуйте, lboss, Вы писали:

    L>>Не совсем понял смысл. В какой это быстрой реализации map работает без итераторов?

    L>>Вот выдержка из реализация std::map в VC7.1

    L>Разачарую тебя, в VC по моему (и не только по моему) самая поганая реализация STL из тех что я видел...


    L>Обычно пишется что-то вроде:


    L>
    L>SecondType & operator[] (const KeyType & key)
    L>{
    L>   TreePair * p = m_root;
    L>   <Поиск по дереву (целиком на указателях)>
    L>   <если не найдено - то вставка>
    L>   return p->second;
    L>}
    
    L>iterator find(const KeyType & key)
    L>{
    L>   TreePair * p = m_root;
    L>   <Поиск по дереву (целиком на указателях)>
    L>   return iterator(p);
    L>}
    
    L>

    Итератор — это ведь может быть класс с одним полем, указателем. Соответственно, время работы с ним (конструирование/копирование/уничтожение) вроде не должно отличаться от скорости работы с указателем (компилятор все проинлайнит, т.е реализация с итератором и указателем не будут отличаться). Или это не так?


    И еще: Твое утверждение насчет поганной STL у VC обоснованно для VC7.1? Т.е, имеет ли смысл менять STL у VC7.1?
    7. О чем невозможно говорить, о том следует молчать.
    Re[11]: Можно сильнее извратиться....
    От: lboss Россия  
    Дата: 13.05.03 11:47
    Оценка:
    Здравствуйте, WFrag, Вы писали:

    WF>Итератор — это ведь может быть класс с одним полем, указателем. Соответственно, время работы с ним (конструирование/копирование/уничтожение) вроде не должно отличаться от скорости работы с указателем (компилятор все проинлайнит, т.е реализация с итератором и указателем не будут отличаться). Или это не так?



    WF>И еще: Твое утверждение насчет поганной STL у VC обоснованно для VC7.1? Т.е, имеет ли смысл менять STL у VC7.1?



    Ну ладно — уел — на VC7.1 всё лучше...
    С уважением Вадим.
    Re[7]: И ты тоже не убедил....
    От: c-smile Канада http://terrainformatica.com
    Дата: 13.05.03 20:12
    Оценка:
    Здравствуйте, limax...

    Призрак бродит по Европе...
    http://www.codeproject.com/useritems/jlwindows.asp
    Re: По мотивам SWL
    От: WolfHound  
    Дата: 16.05.03 10:11
    Оценка: 6 (1)
    Здравствуйте, WolfHound, Вы писали:

    MessageProcessor.h
    #pragma once
    template<class T, uint id_>
    struct Proxy
    {
        typedef void(T::*ProcType)(Msg<id_>&);
        ProcType proc_;
        Proxy(ProcType proc)
            :proc_(proc)
        {}
        bool operator()(T* t, WndMsg* msg)const
        {
            Msg<id_> msg_(msg);
            (t->*proc_)(msg_);
            return msg->handled;
        }
    };
    struct UntilHandled
    {
        typedef bool result_type;
        template<class Iter>
        result_type operator()(Iter first, Iter last) const
        {
            while(first!=last&&!(*first))
                ++first;
            return first!=last;
        }
    };
    template<class T>
    struct MessageProcessor
    {
        typedef boost::signal2<bool, T*, WndMsg*, UntilHandled>    Proc;
        typedef std::map<uint, boost::shared_ptr<Proc> >        Map;
        typedef Map::iterator                                    MapIter;
        typedef Map::value_type                                    MapVal;
    /*************************************************************************************/
        template<uint id_>
        static void AddHandler(void(T::*fn)(Msg<id_>&))
        {
            MapIter iter=GetMap().find(id_);
            if(iter==GetMap().end())
                iter=GetMap().insert
                (
                    MapVal
                    (
                        id_, 
                        boost::shared_ptr<Proc>(new Proc)
                    )
                ).first;
            iter->second->connect(Proxy<T, id_>(fn));
        }
        static bool Process(T* wnd, WndMsg* msg)
        {
            MapIter iter=GetMap().find(msg->id);
            if(iter!=GetMap().end())
                (*iter->second)(wnd, msg);
            return msg->handled;
        }
    private:
        static Map& GetMap()
        {
            static Map map_;
            return map_;
        }
    };
    template<class T>
    bool ProcessMessage(T* t, WndMsg* msg)
    {
        return MessageProcessor<T>::Process(t, msg);
    }
    
    struct Handler
    {
        template<class T, uint id_>
        Handler(void(T::*fn)(Msg<id_>&))
        {
            MessageProcessor<T>::AddHandler(fn);
        }
    };

    window.h
    #pragma once
    #include <boost\signal.hpp>
    #include <boost\smart_ptr.hpp>
    typedef unsigned int uint;
    struct WndMsg
    {
        WndMsg(const int id_)
            :id(id_)
            ,handled(false)
        {}
        const uint id;
        bool handled;
    };
    template<uint id_>
    struct Msg_
    {
        bool& handled;
        Msg_(WndMsg* msg)
            :handled(msg->handled)
        {}
    };
    template<uint id_>
    struct Msg:Msg_<id_>
    {
        Msg(WndMsg* msg)
            :Msg_<id_>(msg)
        {}
    };
    #include "MessageProcessor.h"
    struct Window
    {
        void OnClose(Msg<WM_CLOSE>& msg)
        {
            std::cout<<"SHUTDOWN"<<std::endl;
            msg.handled=true;
        }
        virtual bool ProcessMessage(WndMsg* msg)
        {
            static Handler handlers[]=
            {
                &Window::OnClose,
            };
            return ::ProcessMessage(this, msg);
        }
    };


    #include "stdafx.h"
    #include "window.h"
    #pragma comment(lib, "libboost_signals.lib")
    template<>
    struct Msg<WM_PAINT>:Msg_<WM_PAINT>
    {
        Msg(WndMsg* msg)
            :Msg_<WM_PAINT>(msg)
        {
            std::cout<<"WM_PAINT MSG"<<std::endl;
        }
    };
    struct TestWnd:Window
    {
        void OnPaint1(Msg<WM_PAINT>& msg)
        {
            std::cout<<"WM_PAINT 1"<<std::endl;
        }
        void OnPaint2(Msg<WM_PAINT>& msg)
        {
            std::cout<<"WM_PAINT 2"<<std::endl;
            msg.handled=true;
        }
        void OnPaint3(Msg<WM_PAINT>& msg)
        {
            std::cout<<"WM_PAINT 3"<<std::endl;
        }
        void OnClose(Msg<WM_CLOSE>& msg)
        {
            std::cout<<"WM_CLOSE"<<std::endl;
        }
        virtual bool ProcessMessage(WndMsg* msg)
        {
            static Handler handlers[]=
            {
                &TestWnd::OnPaint1,
                &TestWnd::OnPaint2,
                &TestWnd::OnPaint3,
                &TestWnd::OnClose,
            };
            return        ::ProcessMessage(this, msg)
                ||Window::ProcessMessage(msg);
        }
    };
    int main()
    {
        TestWnd test;
        Window* wnd=&test;
        WndMsg msg(WM_PAINT);
        std::cout<<wnd->ProcessMessage(&msg)<<std::endl;
        WndMsg msg1(WM_CLOSE);
        std::cout<<wnd->ProcessMessage(&msg1)<<std::endl;
        return 0;
    }
    Пусть это будет просто:
    просто, как только можно,
    но не проще.
    (C) А. Эйнштейн
    Re[2]: По мотивам SWL
    От: limax Эстония http://mem.ee
    Дата: 16.05.03 11:11
    Оценка:
    Здравствуйте, WolfHound, Вы писали:
    А зачем всё это? Чем лучше предыдущего варианта? Комментарии?

    Кстати, lboss-овскую неявную карту сообщений прикрутить не захотел? Я использую уже.
    Have fun: Win+M, Ctrl+A, Enter
    Re[3]: По мотивам SWL
    От: WolfHound  
    Дата: 16.05.03 11:32
    Оценка:
    Здравствуйте, limax, Вы писали:

    L>А зачем всё это? Чем лучше предыдущего варианта? Комментарии?

    Поддержка нескольких обработчиков для обного сообщения. Они будут вызваны в порядке перечисления в списке обработчиков. Причем если один из обработчиков сказал что он обработал это сообщение то другие его(сообщение)не получат. Можно еще исключения глушить... но я подумал что это не нужно.

    L>Кстати, lboss-овскую неявную карту сообщений прикрутить не захотел? Я использую уже.

    Мне показалось что это не очень удобно. Ибо у меня нет жилания в каждом обработчике писать return. Вот если будут статические конструкторы и шаблонные typedef'ы тогда другое дело.
    Пусть это будет просто:
    просто, как только можно,
    но не проще.
    (C) А. Эйнштейн
    Re[4]: По мотивам SWL
    От: limax Эстония http://mem.ee
    Дата: 16.05.03 12:25
    Оценка:
    Здравствуйте, WolfHound, Вы писали:

    WH>Поддержка нескольких обработчиков для обного сообщения. Они будут вызваны в порядке перечисления в списке обработчиков. Причем если один из обработчиков сказал что он обработал это сообщение то другие его(сообщение)не получат. Можно еще исключения глушить... но я подумал что это не нужно.

    Тоже интересно, только не могу придумать, где это может понадобиться. Всё равно ведь для базового класса пишешь вызов ||Window::ProcessMessage(msg);
    А вот overhead карты растёт.

    L>>Кстати, lboss-овскую неявную карту сообщений прикрутить не захотел? Я использую уже.

    WH>Мне показалось что это не очень удобно. Ибо у меня нет жилания в каждом обработчике писать return.
    На мой взгляд, возращать везде bool нагляднее, чем устанавливать флажок в Msg.
    Кстати, ещё можно и со специализацией MsgOut (MsgResult в оригинале) поизвращаться.
    Очень гибкая схема получается. Вот только проблема всех гибкостей — найти применение.

    Ха! Задействовав вместо map аналог multimap можно организовать те же цепочки. Хе-хе, причём как! Просто учитывая порядок декларации обработчиков. Интересно, С++ гарантирует конструирование шаблонов возвращяемых значений в порядке декларации методов класса или нет?
    MsgOut<This,WM_USER,__COUNTER__> onMsg(Msg<WM_USER>&);
    MsgOut<This,WM_USER,__COUNTER__> onMsg(Msg<WM_USER>&);//в каком порядке будут сконструированы MsgOut?

    Правда всё равно не вижу, зачем такая цепочка может понадобиться.
    Have fun: Win+M, Ctrl+A, Enter
    Re[5]: По мотивам SWL
    От: WolfHound  
    Дата: 16.05.03 15:27
    Оценка:
    Здравствуйте, limax, Вы писали:

    L>Тоже интересно, только не могу придумать, где это может понадобиться.

    Я тоже но просто тренеруюсь.
    L>Всё равно ведь для базового класса пишешь вызов ||Window::ProcessMessage(msg);
    В смысле? Ведь если сообщение перехвачено то в базовый класс оно не пойдет.
    L>А вот overhead карты растёт.
    Да какой там оверхед особенно если учесть что большенство обработчаков будут давольно тяжолыми.

    L>На мой взгляд, возращать везде bool нагляднее, чем устанавливать флажок в Msg.

    Не флажок надо заменить на метод.
    L>Кстати, ещё можно и со специализацией MsgOut (MsgResult в оригинале) поизвращаться.
    L>Очень гибкая схема получается. Вот только проблема всех гибкостей — найти применение.
    А чего там можно еще прикрутить?

    L>Ха! Задействовав вместо map аналог multimap можно организовать те же цепочки. Хе-хе, причём как!

    Ха! С неявной картой не выдет!
    L>Просто учитывая порядок декларации обработчиков. Интересно, С++ гарантирует конструирование шаблонов возвращяемых значений в порядке декларации методов класса или нет?
    Не знаю.
    L>
    L>MsgOut<This,WM_USER,__COUNTER__> onMsg(Msg<WM_USER>&);
    L>MsgOut<This,WM_USER,__COUNTER__> onMsg(Msg<WM_USER>&);//в каком порядке будут сконструированы MsgOut?
    L>

    Даже есль забыть что __COUNTER__ это не стандарт то это всеравно не скомпилится ибо функции различаются только возвращаемым значением.
    ... << RSDN@Home 1.0 beta 6a >>
    Пусть это будет просто:
    просто, как только можно,
    но не проще.
    (C) А. Эйнштейн
    Re[6]: По мотивам SWL
    От: limax Эстония http://mem.ee
    Дата: 16.05.03 15:56
    Оценка:
    Здравствуйте, WolfHound, Вы писали:

    L>>Кстати, ещё можно и со специализацией MsgOut (MsgResult в оригинале) поизвращаться.

    L>>Очень гибкая схема получается. Вот только проблема всех гибкостей — найти применение.
    WH>А чего там можно еще прикрутить?
    Хз. Если б знал — не гадал бы.

    WH>Даже есль забыть что __COUNTER__ это не стандарт то это всеравно не скомпилится ибо функции различаются только возвращаемым значением.

    Тьфу, забыл. Сморозил.
    А так?
    MultiMsgOut<This,WM_USER,1> onMsg(MultiMsg<WM_USER,1>&);
    MultiMsgOut<This,WM_USER,2> onMsg(MultiMsg<WM_USER,2>&);

    Из взломщика вызывать не onMsg(Msg<...>), а onMsg(MultiMsg<...>), и т.д.
    Можно использовать другой метод унификации. Дома додумаю...
    Have fun: Win+M, Ctrl+A, Enter
    Re[7]: По мотивам SWL
    От: WolfHound  
    Дата: 16.05.03 16:24
    Оценка:
    Здравствуйте, limax, Вы писали:

    L>
    L>MultiMsgOut<This,WM_USER,1> onMsg(MultiMsg<WM_USER,1>&);
    L>MultiMsgOut<This,WM_USER,2> onMsg(MultiMsg<WM_USER,2>&);
    L>

    L>Из взломщика вызывать не onMsg(Msg<...>), а onMsg(MultiMsg<...>), и т.д.
    L>Можно использовать другой метод унификации. Дома додумаю...
    #define MESSAGE__(class, id, id2)\
    MultiMsgOut<class,id,id2> onMsg(MultiMsg<id,id2>& msg);
    #define MESSAGE_(class, id, id2)MESSAGE__(class, id, id2)
    #define MESSAGE(id)MESSAGE_(CUR_CLASS, id, __COUNTER__)

    А использовать так
    class Test
    {
        #define CUR_CLASS Test
        MESSAGE(WM_PAINT)
        {
            return false;
        }
        MESSAGE(WM_PAINT)
        {
            return false;
        }
        #undef CUR_CLASS
    };

    Но всеравно жутко смотрится.
    ... << RSDN@Home 1.0 beta 6a >>
    Пусть это будет просто:
    просто, как только можно,
    но не проще.
    (C) А. Эйнштейн
    Re[5]: По мотивам SWL
    От: lboss Россия  
    Дата: 19.05.03 04:35
    Оценка:
    Здравствуйте, limax, Вы писали:

    L>Кстати, ещё можно и со специализацией MsgOut (MsgResult в оригинале) поизвращаться.


    Кстати я у себя MsgResult переименовал в OnMessage — так по моему наглядней:


     OnMessage<WM_CLOSE>  onMsg(Msg<WM_CLOSE> & msg);
     OnCommand<IDOK>  onMsg(Msg<WM_COMMAND> & msg);
    С уважением Вадим.
    Re[4]: По мотивам SWL
    От: lboss Россия  
    Дата: 19.05.03 04:44
    Оценка:
    Здравствуйте, WolfHound, Вы писали:

    WH>Поддержка нескольких обработчиков для обного сообщения. Они будут вызваны в порядке перечисления в списке обработчиков. Причем если один из обработчиков сказал что он обработал это сообщение то другие его(сообщение)не получат. Можно еще исключения глушить... но я подумал что это не нужно.


    А зачем несколько обработчиков?! Интересно а как ты BeginPaint откатишь? Не проще ли одну функцию на класс заводить и вызывать из неё несколько своих?


    
    OnMessage<WM_PAINT>  onMsg(Msg<WM_PAINT> & msg)
    {
      return myFirstPaint(msg) || mySecondPaint(msg);
    }
    С уважением Вадим.
    Re[6]: По мотивам SWL
    От: limax Эстония http://mem.ee
    Дата: 19.05.03 07:21
    Оценка:
    Здравствуйте, lboss, Вы писали:
    L> OnMessage<WM_CLOSE>  onMsg(Msg<WM_CLOSE> & msg);
    L> OnCommand<IDOK>  onMsg(Msg<WM_COMMAND> & msg);

    А куда название класса дел? Или справился с вызовом инициализатора?
    OnMessage<This,WM_CLOSE> ...
    Have fun: Win+M, Ctrl+A, Enter
    Re[7]: По мотивам SWL
    От: lboss Россия  
    Дата: 20.05.03 07:39
    Оценка:
    Здравствуйте, limax, Вы писали:

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

    L>
    L>> OnMessage<WM_CLOSE>  onMsg(Msg<WM_CLOSE> & msg);
    L>> OnCommand<IDOK>  onMsg(Msg<WM_COMMAND> & msg);
    L>

    L>А куда название класса дел? Или справился с вызовом инициализатора?
    L>OnMessage<This,WM_CLOSE> ...

    Нет просто упростил — я всё таки макросы заюзал:

    #define VMessageMap(ThisClassName, BaseClassName)\
        typedef ThisClassName ThisClass;\
        virtual    bool    processMessage(MsgData & msg)\
        {\
            return MsgProcessor<ThisClass>::getInstance().processMessage(this, msg) || BaseClassName::processMessage(msg);\
        }
    
    #define VOnMessage(MessageId)\
        OnMessage<ThisClass, MessageId> onMsg(Msg<MessageId> & msg)
    
    #define VOnCommand(CommandId)\
        OnCommand<ThisClass, CommandId> onMsg(CommandMsg<CommandId> & msg)


    Получается код:


    class Test1 : public    Wnd
    {
    public:
        VMessageMap(Test1, Wnd);
            VOnMessage(WM_PAINT)
            {
                puts("VOnMessage(WM_PAINT)");
                return true;
            }
            VOnCommand(IDOK)
            {
                EndDialog(IDOK);
                return true;
            }
    
    };
    С уважением Вадим.
    Re[8]: По мотивам SWL
    От: limax Эстония http://mem.ee
    Дата: 28.05.03 19:39
    Оценка:
    Здравствуйте, lboss, Вы писали:
    L>#define VOnCommand(CommandId)\
    L>    OnCommand<ThisClass, CommandId> onMsg(CommandMsg<CommandId> & msg)

    Для OnCommand ещё одну карту создаёшь?
    Have fun: Win+M, Ctrl+A, Enter
    Re[9]: По мотивам SWL
    От: lboss Россия  
    Дата: 29.05.03 12:20
    Оценка:
    Здравствуйте, limax, Вы писали:

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

    L>
    L>>#define VOnCommand(CommandId)\
    L>>    OnCommand<ThisClass, CommandId> onMsg(CommandMsg<CommandId> & msg)
    L>

    L>Для OnCommand ещё одну карту создаёшь?

    Нет — поюзал виртуальность...
    С уважением Вадим.
    Re[10]: По мотивам SWL
    От: limax Эстония http://mem.ee
    Дата: 29.05.03 18:41
    Оценка:
    Здравствуйте, lboss, Вы писали:
    L>>Для OnCommand ещё одну карту создаёшь?

    L>Нет — поюзал виртуальность...

    Каким образом? Поподробнее, если можно.
    Have fun: Win+M, Ctrl+A, Enter
    Re[11]: По мотивам SWL
    От: lboss Россия  
    Дата: 30.05.03 03:49
    Оценка:
    Здравствуйте, limax, Вы писали:

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

    L>>>Для OnCommand ещё одну карту создаёшь?

    L>>Нет — поюзал виртуальность...

    L>Каким образом? Поподробнее, если можно.

    Просто создал базовый класс с виртуальной функцией — она проосто процессит сообщение и говорит смогла или нет. А в карте сообщений заменил map на std::hash_map<UINT, std::list<VMessageProcessorBase *> >. Ну ещё приоритеты добавил...
    С уважением Вадим.
    Re[12]: По мотивам SWL
    От: WolfHound  
    Дата: 30.05.03 09:02
    Оценка:
    Здравствуйте, lboss, Вы писали:

    L>Просто создал базовый класс с виртуальной функцией — она проосто процессит сообщение и говорит смогла или нет. А в карте сообщений заменил map на std::hash_map<UINT, std::list<VMessageProcessorBase *> >. Ну ещё приоритеты добавил...


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

    WH>Может выложишь что у тебя получилось, а мы посмотрим.


    Нет — там целая библиотека...
    С уважением Вадим.
    Re[14]: По мотивам SWL
    От: WolfHound  
    Дата: 02.06.03 11:55
    Оценка:
    Здравствуйте, lboss, Вы писали:

    L>Нет — там целая библиотека...

    В смысле? Не имеешь права или боишся что не влезет? Если второе то заархивируй и загрузи на сервер.
    Пусть это будет просто:
    просто, как только можно,
    но не проще.
    (C) А. Эйнштейн
    Re[15]: По мотивам SWL
    От: lboss Россия  
    Дата: 03.06.03 03:52
    Оценка:
    Здравствуйте, WolfHound, Вы писали:

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


    L>>Нет — там целая библиотека...

    WH>В смысле? Не имеешь права или боишся что не влезет? Если второе то заархивируй и загрузи на сервер.

    А зачем это? Просто у меня есть своя библиотека (vlib) там много кода (в том числе и эксперементального) — оформлять и вычищать это нет времени — а вам эта куча мало что даст... Основные принципы работы с сообщениями я уже в флейме указал — так, что зачем выгружать всё библиотеку?!
    С уважением Вадим.
    Re[16]: По мотивам SWL
    От: yaroslav_v http://yaroslav-v.chat.ru
    Дата: 03.06.03 07:12
    Оценка:
    L>А зачем это? Просто у меня есть своя библиотека (vlib) там много кода (в том числе и эксперементального) — оформлять и вычищать это нет времени — а вам эта куча мало что даст... Основные принципы работы с сообщениями я уже в флейме указал — так, что зачем выгружать всё библиотеку?!

    Достаточно пары файлов касающихся окон — не беда если это прямо не будет компилиться,
    просто чем больше кода — тем понятнее, конечно у каждого накоплено много своего
    кода и обычно в виде *.lib — файла, если ты не хочешь все публиковать — будет интересно
    посмотреть часть, касаюэуюся SWL-modified.
    Re[17]: Альтернативные раелизации
    От: Kluev  
    Дата: 03.06.03 18:04
    Оценка:
    Итак в ветке были рассмотрены три основных архитектуры обработки сообщений
    1 — явные карты сообщений
    2 — неявные карты сообщений
    3 — базовый класс с виртуальными функциями-обработчиками
    Третья была сразу отброшена как устаревщая и неэффективная.

    Есть еще (как минимум) один пропущенный вариант: использования псевдо-виртуальныех функций
    template <class T>
    // базовый класс
    struct WndBase {
    
        void msg_handle( UINT id, WPARAM w, LPARAM l ) {
            T *p = static_cast<T*>( this );
            switch ( id ) {
                case WM_PAINT: p->wm_paint(); break;
                case WM_MOVE: p->wm_move(LOWORD(l),HIWORD(l)); break;
                case WM_CREATE: p->wm_create(*(CREATESTRUCT*)l); break;
                // .... все виндовые сообщения
            }
        }
    
        void wm_paint() {}
        void wm_create( CREATESTRUCT&) {}
        void wm_move(int x, int y) {}
        // .... все виндовые сообщения
    };
    
    // производный
    struct MyWnd : public WndBase<MyWnd> {
        void wm_move( int x, int y ) {
            // ....
        }
    };

    В этой реализации нет таких накладных расходов как при использовании виртуальных функций. Недостатки: необходимость обьявления всех обработчиков в базовом классе и switch в функции msg_handle (слишком длинный получится) Плюсом является то, что отпадает геморой связаный с использованием макросов и написанием завернутых сигнатур сообщений типа MsgResult<MyClass,WM_PAINT> onMsg( Msg<WM_PAINT>& ) и открывается путь к использованию нормальных сигнатур функций обработчиков wm_move(int x, int y).

    В этой реализации всю картину портит свитч по всем виндовым сообщениям. От него можно отказатся так как помещение всех обработчиков в базовый класс открывает новые возможности к созданию карты сообщений. В частности можно путем сравнения указателей (&WndBase::wm_paint == &T::wm_paint) определить перегрузил ли пользователь функцию в дочернем классе. Это позволит неявно создавать карты сообщений и вкусить удобство от нормальных сигнатур обработчиков

    struct WindowBase {
        void wm_paint()        { assert(0); } // не для вызова
        void wm_ncpaint()    { assert(0); }
        void wm_create()    { assert(0); }
        void wm_nccreate()    { assert(0); }
        void wm_size()        { assert(0); }
        void wm_move()        { assert(0); }
        // добавить все остальные сообщения
        void def_handler() {
            printf("def_handler\n");
        }
    
        virtual void msg_handle( UINT msg )    = 0;
    };
    
    typedef void (WindowBase::*XMFn)();
    
    #define XANDLER_CHK( xfn, id ) if ( (XMFn)&T::xfn != (XMFn)&WindowBase::xfn ) { mmp[id] = (XMFn)&T::xfn; }
    
    template <class T>
    class Window : public WindowBase {
        typedef std::map<UINT,XMFn>    mp_id2xf;
    public:
    
        void msg_handle( UINT msg ) {
            static mp_id2xf mmp;
            static bool init = true;
            if ( init ) {
                init = false;
                XANDLER_CHK( wm_paint, WM_PAINT );
                XANDLER_CHK( wm_ncpaint, WM_NCPAINT );
                XANDLER_CHK( wm_create, WM_CREATE );
                XANDLER_CHK( wm_nccreate, WM_NCCREATE );
                XANDLER_CHK( wm_size, WM_SIZE );
                XANDLER_CHK( wm_move, WM_MOVE );
            // добавить все остальные сообщения
            }
            mp_id2xf::iterator x = mmp.find( msg );
            if ( mmp.end() == x ) {
                def_handler();
            }
            else {
                WindowBase &p = *this;
                (p.*x->second)();
            }
                
        }
    
    };
    
    struct MyWnd : Window<MyWnd> {
        void wm_paint() {
            printf("paint\n");
        }
        void wm_move() {
            printf("move\n");
        }
    
    };
    
    void test() {
        MyWnd wnd;
        wnd.msg_handle( WM_PAINT ); // MyWnd::wm_paint
        wnd.msg_handle( WM_MOVE );  // MyWnd::wm_move 
        wnd.msg_handle( WM_CREATE );// WindowBase::def_handler
    }


    По идее, конечно, надо хранить в карте указатель не на обработчик, а на взломщик из которого уже будет вызыватся функция-обработчик:
    void wm_crak_size( WPARAM, LPARAM l ) {
        T *p = static_cast<T*>(this);
        p->wm_size( LOWORD(l), HIWORD(l) );
    }
    Re[18]: Альтернативные раелизации
    От: lboss Россия  
    Дата: 04.06.03 04:10
    Оценка:
    Здравствуйте, Kluev, Вы писали:

    <Skipped...>

    Честно говоря это первая из реализаций, которая мне пришла в голову, но подумав я её отбросил по причинам:

    1. Сообщений много — инстанциирование шаблона будет происходить каждый раз — это сделает процесс компиляции невыносимым...
    2. Проблемма наследника от наследника: например диалог наследник окна. Но ведь от диалого тоже кто-то отнаследуется... В общем получается, что весь код — это шаблон (а-ля WTL) — это терпимо на не больших проектах, но становится невыносимым при укреплении проекта... А учитывая п.1 вообще нежизненно... Плюс отсутсвие нормального полиморфизма — иногда очень бесит. В общем стиль на любителя...
    С уважением Вадим.
    Re: По мотивам SWL
    От: Аноним  
    Дата: 05.06.03 12:25
    Оценка:
    Здравствуйте, WolfHound, Вы писали:

    А как в шаблон сообщения добавить char* параметр?, почему-то на этапе линковки проблемы возникают
    Re[17]: По мотивам SWL
    От: lboss Россия  
    Дата: 06.06.03 03:26
    Оценка:
    Здравствуйте, yaroslav_v, Вы писали:

    L>>А зачем это? Просто у меня есть своя библиотека (vlib) там много кода (в том числе и эксперементального) — оформлять и вычищать это нет времени — а вам эта куча мало что даст... Основные принципы работы с сообщениями я уже в флейме указал — так, что зачем выгружать всё библиотеку?!


    _>Достаточно пары файлов касающихся окон — не беда если это прямо не будет компилиться,

    _>просто чем больше кода — тем понятнее, конечно у каждого накоплено много своего
    _>кода и обычно в виде *.lib — файла, если ты не хочешь все публиковать — будет интересно
    _>посмотреть часть, касаюэуюся SWL-modified.


    Ну ладно — вот файл (он не компилится):

    #ifndef __vwnd_h_
    #define __wwnd_h_
    
    #include <map>
    #include <list>
    #include "vbase.h"
    #include "vmodule.h"
    #include "vcontent.h"
    #include "vgc.h"
    
    struct VMsgData
    {
        HWND    m_hWnd;
        UINT    m_msg;
        WPARAM    m_wParam;
        LPARAM    m_lParam;
    
        VMsgData(const VMsgData & o)
            : m_hWnd(o.m_hWnd), m_msg(o.m_msg), m_wParam(o.m_wParam), m_lParam(o.m_lParam)
        {
        }
        VMsgData(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
            : m_hWnd(hWnd), m_msg(msg), m_wParam(wParam), m_lParam(lParam)
        {
        }
    };
    
    template <UINT msgID>
    struct VMsg : public VMsgData
    {
        VMsg(const VMsgData & o)
            : VMsgData(o)
        {
        }
    };
    
    template <WPARAM ctrlID>
    struct VCommandMsg : public VMsgData
    {
        VCommandMsg(const VMsgData & o)
            : VMsgData(o)
        {
        }
        HWND    sender() const { return (HWND)m_lParam; }
        WORD    id() const { return LOWORD(m_wParam); }
        WORD    notificationCode() const { return HIWORD(m_wParam); }
    };
    
    template <WPARAM ctrlID>
    struct VNotifyMsg : public VMsgData
    {
        VNotifyMsg(const VMsgData & o)
            : VMsgData(o)
        {
        }
        int        sender() const { return (int)m_wParam; }
        LPNMHDR    addInfo() const { return (LPNMHDR)m_lParam; }
    };
    
    class VWnd;
    
    class VMessageMapProcessor
    {
    public:
        virtual    LRESULT    operator () (VWnd * obj, VMsgData & msgData, bool & bHandled) = 0;
    private:
        int    m_order;
        friend    class VMessageMapBase;
    };
    
    class VMessageMapBase
    {
    public:
        typedef std::list<VMessageMapProcessor *> TList;
        typedef std::map<UINT, TList> TMap;
    
        static    void    add(TMap & rMap, UINT msgID, VMessageMapProcessor * f, int order = 0)
        {
            TList & l = rMap[msgID];
            for(TList::iterator p = l.end(); p != l.begin(); )
            {
                --p;
                if((*p)->m_order <= order)
                {
                    l.insert(p, f);
                    return;
                }
            }
            l.push_front(f);
        }
        static    void    remove(TMap & rMap, UINT msgID, VMessageMapProcessor * f)
        {
            TList & l = rMap[msgID];
            l.remove(f);
        }
        static    LRESULT    processWindowMessage(TMap & rMap, VWnd * obj, VMsgData & msgData, bool & bHandled)
        {
            TMap::iterator pList = rMap.find(msgData.m_msg);
            if(pList != rMap.end())
            {
                TList & l = pList->second;
                for(TList::iterator p = l.begin(); p != l.end(); ++p)
                {
                    bHandled = true;
                    LRESULT ret = (**p)(obj, msgData, bHandled);
                    if(bHandled)
                        return ret;
                }
            }
            bHandled = false;
            return 0;
        }
    };
    
    template <class TWnd>
    class VMessageMap : public VMessageMapBase
    {
    public:
        static    void    add(UINT msgID, VMessageMapProcessor * f, int order = 0)
        {
            VMessageMapBase::add(getMap(), msgID, f, order);
        }
        static    LRESULT    processWindowMessage(TWnd * obj, VMsgData & msgData, bool & bHandled)
        {
            return VMessageMapBase::processWindowMessage(getMap(), obj, msgData, bHandled);
        }
    protected:
        static    TMap & getMap()
        {
            static    TMap m_map;
            return m_map;
        }
    };
    class VOnMessageBase
    {
    public:
        VOnMessageBase(LRESULT _ret)
            : m_ret(_ret)
        {
        }
        operator LRESULT () const
        {
            return m_ret;
        }
    protected:
        LRESULT    m_ret;
    };
    
    template <class TRel, UINT msgID>
    class VOnMessage : public VOnMessageBase
    {
    public:
        VOnMessage(LRESULT _ret)
            : VOnMessageBase(_ret)
        {
            m_Registrator.fake();
        }
        class Registrator : public VMessageMapProcessor
        {
        public:
            Registrator()
            {
                VMessageMap<TRel>::add(msgID, this);
            }
            virtual    LRESULT    operator () (VWnd * obj, VMsgData & msgData, bool & bHandled)
            {
                return static_cast<TRel *>(obj)->onMessage(VMsg<msgID>(msgData), bHandled);
            }
            void    fake() {}
        };
        static Registrator    m_Registrator;
    };
    template <class TRel, UINT msgID>
    typename VOnMessage<TRel, msgID>::Registrator VOnMessage<TRel, msgID>::m_Registrator;
    
    template <class TRel, WPARAM contID>
    class VOnCommand : public VOnMessageBase
    {
    public:
        VOnCommand(LRESULT _ret)
            : VOnMessageBase(_ret)
        {
            m_Registrator.fake();
        }
        class Registrator : public VMessageMapProcessor
        {
        public:
            Registrator()
            {
                VMessageMap<TRel>::add(WM_COMMAND, this);
            }
            virtual    LRESULT    operator () (VWnd * obj, VMsgData & msgData, bool & bHandled)
            {
                if(msgData.m_wParam == contID)
                    return static_cast<TRel *>(obj)->onMessage(VCommandMsg<contID>(msgData), bHandled);
                bHandled = false;
                return 0;
            }
            void    fake() {}
        };
        static Registrator    m_Registrator;
    };
    template <class TRel, WPARAM contID>
    typename VOnCommand<TRel, contID>::Registrator VOnCommand<TRel, contID>::m_Registrator;
    
    template <class TRel, WPARAM contID>
    class VOnNotify : public VOnMessageBase
    {
    public:
        VOnNotify(LRESULT _ret)
            : VOnMessageBase(_ret)
        {
            m_Registrator.fake();
        }
        class Registrator : public VMessageMapProcessor
        {
        public:
            Registrator()
            {
                VMessageMap<TRel>::add(WM_NOTIFY, this);
            }
            virtual    LRESULT    operator () (VWnd * obj, VMsgData & msgData, bool & bHandled)
            {
                if(((LPNMHDR)msgData.m_lParam)->code == contID)
                    return static_cast<TRel *>(obj)->onMessage(VNotifyMsg<contID>(msgData), bHandled);
                bHandled = false;
                return 0;
            }
            void    fake() {}
        };
        static Registrator    m_Registrator;
    };
    template <class TRel, WPARAM contID>
    typename VOnNotify<TRel, contID>::Registrator VOnNotify<TRel, contID>::m_Registrator;
    //--------------------------- Messages -----------------------------
    template <>
    struct VMsg<WM_INITDIALOG> : public VMsgData
    {
        VMsg(const VMsgData & msg)
            : VMsgData(msg)
        {
        }
        HWND    defControl() const { return (HWND)m_wParam; }
        void *    param() const { return (void *)m_lParam; }
    };
    template <>
    struct VMsg<WM_COMMAND> : public VMsgData
    {
        VMsg(const VMsgData & msg)
            : VMsgData(msg)
        {
        }
        HWND    sender() const { return (HWND)m_lParam; }
        WORD    id() const { return LOWORD(m_wParam); }
        WORD    notificationCode() const { return HIWORD(m_wParam); }
    };
    template <>
    struct VMsg<WM_PAINT> : public VMsgData
    {
        VMsg(const VMsgData & msg)
            : VMsgData(msg)
        {
        }
    
        PAINTSTRUCT    m_ps;
    
        HDC    begin(HWND hWnd = NULL)
        {
            return BeginPaint(hWnd ? hWnd : m_hWnd, &m_ps);
        }
        void    end(HWND hWnd = NULL)
        {
            EndPaint(hWnd ? hWnd : m_hWnd, &m_ps);
        }
    };
    
    #define VMESSAGE_MAP(ClassName, BaseClass)\
        typedef ClassName ThisClass;\
        virtual    LRESULT    processWindowMessage(VMsgData & msgData, bool & bHandled)\
        {\
            LRESULT ret = VMessageMap<ClassName>::processWindowMessage(this, msgData, bHandled);\
            if(!bHandled)\
                ret = BaseClass::processWindowMessage(msgData, bHandled);\
            return ret;\
        }
    
    #define VON_MESSAGE(msgID)\
        VOnMessage<ThisClass, msgID>    onMessage(VMsg<msgID> & msg, bool & bHandled)
    
    #define VON_COMMAND(cmdID)\
        VOnCommand<ThisClass, cmdID>    onMessage(VCommandMsg<cmdID> & msg, bool & bHandled)
    
    #define VON_NOTIFY(msgID)\
        VOnNotify<ThisClass, msgID>    onMessage(VNotifyMsg<msgID> & msg, bool & bHandled)
    
    class VWnd
    {
    public:
        typedef VWnd ThisClass;
        virtual    LRESULT    processWindowMessage(VMsgData & msgData, bool & bHandled)
        {
            return VMessageMap<VWnd>::processWindowMessage(this, msgData, bHandled);
        }
    
        static    std::map<LPARAM, VWnd *> &    hWnd2ObjMap()
        {
            static    std::map<LPARAM, VWnd *> m_instance;
            return m_instance;
        }
    
        HWND    m_hWnd;
        operator HWND () const { return m_hWnd; }
    
        VRet<LONG>    setWindowLong(int index, LONG newValue)
        {
            SetLastError(0);
            LONG ret = SetWindowLong(m_hWnd, index, newValue);
            return VRet<LONG>::errIfError(ret, !ret && GetLastError(), "error in SetWindowLong()");
        }
        VRet<BOOL>    destroyWindow()
        {
            return VRet<BOOL>::errIfEqu(DestroyWindow(m_hWnd), FALSE, "Error on destroyWindow()");
        }
    
        BOOL    isWindow() const
        {
            return IsWindow(m_hWnd);
        }
        BOOL    show(bool bShow = true)
        {
            return ShowWindow(m_hWnd, bShow ? SW_SHOW : SW_HIDE);
        }
        //MessageMap
        VON_MESSAGE(WM_NCDESTROY)
        {
            onNCDestroy();
            return 0;
        }
        virtual    void    onNCDestroy()
        {
            m_hWnd = NULL;
        }
        RECT    getRect() const
        {
            RECT rect;
            VRTVERIFYE(GetWindowRect(m_hWnd, &rect));
            return rect;
        }
        RECT    getRect(HWND relativeTo) const
        {
            RECT rect;
            VRTVERIFYE(GetWindowRect(m_hWnd, &rect));
            VRTVERIFYE(MapWindowPoints(NULL, relativeTo, (LPPOINT)&rect, 2) != 0);
            return rect;
        }
        static LRESULT CALLBACK    wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
        {
            if(msg == WM_CREATE)
            {
                LPCREATESTRUCT cs = reinterpret_cast<LPCREATESTRUCT>(lParam);
                VWnd * win = reinterpret_cast<VWnd *>(cs->lpCreateParams);
                win->m_hWnd = hWnd;
                hWnd2ObjMap()[(LPARAM)hWnd] = win;
            }
            std::map<LPARAM, VWnd *>::const_iterator pWnd = hWnd2ObjMap().find((LPARAM)hWnd);
            if(pWnd != hWnd2ObjMap().end())
            {
                VWnd * win = pWnd->second;
                if(win)
                {
                    bool bHandled = true;
                    LRESULT ret = win->processWindowMessage(VMsgData(hWnd, msg, wParam, lParam), bHandled);
                    if(bHandled)
                        return ret;
                    else
                        return win->defWndProc(hWnd, msg, wParam, lParam);
                }
            }
            return ::DefWindowProc(hWnd, msg, wParam, lParam);
        }
        virtual    LRESULT defWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
        {
            return ::DefWindowProc(hWnd, msg, wParam, lParam);
        }
        static ATOM registerClass()
        {
            WNDCLASSEX wcex;
    
            memset(&wcex, 0, sizeof(wcex));
    
            wcex.cbSize            = sizeof(WNDCLASSEX); 
            wcex.style            = CS_HREDRAW | CS_VREDRAW;
            wcex.lpfnWndProc    = wndProc;
            wcex.cbClsExtra        = 0;
            wcex.cbWndExtra        = 0;
            wcex.hInstance        = (HINSTANCE)_ThisModule.m_hModule;
            wcex.hIcon            = NULL;
            wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
            wcex.hbrBackground    = (HBRUSH)(COLOR_WINDOW+1);
            wcex.lpszMenuName    = NULL;
            wcex.lpszClassName    = "VWindowClass";
            wcex.hIconSm        = NULL;
            return RegisterClassEx(&wcex);
        }
    
        virtual    const SYSCHAR *    getClassName()
        {
            static ATOM wndAtom = registerClass();
            VASSERTE(wndAtom);
            return (const SYSCHAR *)wndAtom;
        }
    
        virtual    DWORD    getDefStyle()
        {
            return WS_OVERLAPPEDWINDOW;
        }
    
        virtual    DWORD    getDefExStyle()
        {
            return 0;
        }
    
        virtual HWND create(HWND hParent, DWORD dwStyle, DWORD dwExStyle, int X, int Y, int nWidth, int nHeight)
        {
            m_hWnd = CreateWindowEx(
                dwExStyle,
                getClassName(),
                NULL,
                dwStyle, 
                X,
                Y,
                nWidth,
                nHeight,
                hParent,
                NULL,
                _ThisModule.m_hModule,
                this
            );
            VASSERTE(m_hWnd);
            return m_hWnd;
        }
        HWND create(int X, int Y, int nWidth, int nHeight)
        {
            return create(NULL, getDefStyle(), getDefExStyle(), X, Y, nWidth, nHeight);
        }
        HWND create(HWND hParent, DWORD dwStyle, DWORD dwExStyle)
        {
            return create(hParent, dwStyle, dwExStyle, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT);
        }
        void    getWindowRect(LPRECT pRect)
        {
            VVERIFY(GetWindowRect(m_hWnd, pRect), "Error GetWindowRect");
        }
        RECT    getWindowRect()
        {
            RECT rect;
            getWindowRect(&rect);
            return rect;
        }
        void    screenToClient(POINT & pt)
        {
            ScreenToClient(m_hWnd, &pt);
        }
        void    screenToClient(RECT & rect)
        {
            MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rect, 2);
        }
        void    setPos(HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags)
        {
            SetWindowPos(m_hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags);
        }
        void    setPos(HWND hWndInsertAfter, UINT uFlags = SWP_NOMOVE | SWP_NOSIZE)
        {
            setPos(hWndInsertAfter, 0, 0, 0, 0, uFlags);
        }
        void    setPos(int X, int Y, UINT uFlags = SWP_NOZORDER | SWP_NOSIZE)
        {
            setPos(NULL, X, Y, X, Y, uFlags);
        }
    
        BOOL    move(int x, int y, int dx, int dy, BOOL bRepaint = TRUE)
        {
            return MoveWindow(m_hWnd, x, y, dx, dy, bRepaint);
        }
        BOOL    move(RECT & rect, BOOL bRepaint = TRUE)
        {
            return move(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, bRepaint);
        }
        VSysString    windowText()
        {
            VSysString ret;
            size_t len = (size_t)GetWindowTextLength(m_hWnd);
            GetWindowText(m_hWnd, ret.getBuffer(len), (int)len + 1);
            ret.releaseBuffer(len);
            return ret;
        }
        void    windowText(const SYSCHAR * pStr)
        {
            SetWindowText(m_hWnd, pStr);
        }
    };
    
    class VDlgControl
    {
    public:
        int                m_id;
        VDlgControl *    m_layoutParent;
        enum DockType
        {
            dtNone,
            dtTop,
            dtLeft,
            dtBottom,
            dtRight,
            dfClient,
        };
        DockType    m_dock;
        bool        m_anchorTop;
        bool        m_anchorLeft;
        bool        m_anchorBottom;
        bool        m_anchorRight;
        RECT        m_rect;
    
        VDlgControl()
            : m_id(-1), m_layoutParent(NULL),
            m_dock(dtNone),
            m_anchorTop(true), m_anchorLeft(true), m_anchorRight(false), m_anchorBottom(false)
        {
        }
        VDlgControl(int id, VDlgControl * layoutParent = NULL)
            : m_id(id), m_layoutParent(layoutParent),
            m_dock(dtNone),
            m_anchorTop(true), m_anchorLeft(true), m_anchorRight(false), m_anchorBottom(false)
        {
        }
        VDlgControl(int id, DockType dockType, VDlgControl * layoutParent = NULL)
            : m_id(id), m_layoutParent(layoutParent),
            m_dock(dockType),
            m_anchorTop(true), m_anchorLeft(true), m_anchorRight(false), m_anchorBottom(false)
        {
        }
        VDlgControl(int id, bool anchorLeft, bool anchorTop, bool anchorRight, bool anchorBottom, VDlgControl * layoutParent = NULL)
            : m_id(id), m_layoutParent(layoutParent),
            m_dock(dtNone),
            m_anchorTop(anchorTop), m_anchorLeft(anchorLeft), m_anchorRight(anchorRight), m_anchorBottom(anchorBottom)
        {
        }
        virtual ~VDlgControl()
        {
        }
    };
    
    class VControlMap
    {
    public:
        void    add(int cntl, VDlgControl * wnd)
        {
            m_map[cntl] = wnd;
        }
    
    protected:
        friend class VDialog;
        std::map<int, VDlgControl *>    m_map;
    };
    
    template <int ctrlId, class TCtrlClass = VWnd>
    class VControl : public TCtrlClass, public VDlgControl
    {
    public:
        VControl(VDlgControl * layoutParent = NULL)
            : VDlgControl(ctrlId, layoutParent)
        {
            VContent::getInstance().curDialog()->m_controlMap.add(ctrlId, this);
        }
        VControl(DockType dockType, VDlgControl * layoutParent = NULL)
            : VDlgControl(ctrlId, dockType, layoutParent)
        {
            VContent::getInstance().curDialog()->m_controlMap.add(ctrlId, this);
        }
        VControl(bool anchorLeft, bool anchorTop, bool anchorRight, bool anchorBottom, VDlgControl * layoutParent = NULL)
            : VDlgControl(ctrlId, anchorLeft, anchorTop, anchorRight, anchorBottom, layoutParent)
        {
            VContent::getInstance().curDialog()->m_controlMap.add(ctrlId, this);
        }
    };
    //
    class VDialog : public VWnd
    {
    public:
        VDialog()
            : m_bModeless(false)
        {
            VContent::getInstance().curDialog(this);
        }
        VRet<HWND>    createDialog(LPCTSTR lpTemplateName, HWND hWndParent = VContent::getInstance().currentHWND(), HINSTANCE hInstance = _ThisModule.m_hModule)
        {
            m_bModeless = true;
            HWND hWnd = CreateDialogParam(hInstance, lpTemplateName, hWndParent, dlgProc, (LPARAM)this);
            if(hWnd)
                InitModeless::getInstance().addDialog(hWnd);
            return VRet<HWND>::errIfEqu(hWnd, NULL, "Error create dialog");
        }
        VRet<HWND>    createDialog(WORD idd, HWND hWndParent = VContent::getInstance().currentHWND(), HINSTANCE hInstance = _ThisModule.m_hModule)
        {
            m_bModeless = true;
            HWND hWnd = CreateDialogParam(hInstance, MAKEINTRESOURCE(idd), hWndParent, dlgProc, (LPARAM)this);
            if(hWnd)
                InitModeless::getInstance().addDialog(hWnd);
            return VRet<HWND>::errIfEqu(hWnd, NULL, "Error create dialog");
        }
        VRet<INT_PTR>    doModal(LPCTSTR lpTemplateName, HWND hWndParent = VContent::getInstance().currentHWND(), HINSTANCE hInstance = _ThisModule.m_hModule)
        {
            m_bModeless = false;
            return VRet<INT_PTR>::errIfEqu(DialogBoxParam(hInstance, lpTemplateName, hWndParent, dlgProc, (LPARAM)this), -1, "Error create dialog");
        }
        VRet<INT_PTR>    doModal(WORD idd, HWND hWndParent = VContent::getInstance().currentHWND(), HINSTANCE hInstance = _ThisModule.m_hModule)
        {
            m_bModeless = false;
            return VRet<INT_PTR>::errIfEqu(DialogBoxParam(hInstance, MAKEINTRESOURCE(idd), hWndParent, dlgProc, (LPARAM)this), -1, "Error create dialog");
        }
    
        VRet<BOOL>    endDialog(int nResult)
        {
            if(isModeless())
                return destroyWindow();
            else
                return VRet<BOOL>::errIfEqu(EndDialog(m_hWnd, nResult), FALSE, "Error in EndDialog");
        }
        bool    isModeless() const
        {
            return m_bModeless;
        }
        virtual    void    onNCDestroy()
        {
            InitModeless::getInstance().removeDialog(m_hWnd);
            VWnd::onNCDestroy();
        }
    
        virtual    bool onOk()
        {
            return true;
        }
        virtual    bool onCancel()
        {
            return true;
        }
        virtual    bool onClose()
        {
            return true;
        }
    
        VMESSAGE_MAP(VDialog, VWnd);
        VON_COMMAND(IDOK)
        {
            if(!onOk())
                bHandled = false;
            else
                endDialog(IDOK);
            return 0;
        }
    
        VON_COMMAND(IDCANCEL)
        {
            if(!onCancel())
                bHandled = false;
            else
                endDialog(IDCANCEL);
            return 0;
        }
        VON_MESSAGE(WM_CLOSE)
        {
            if(!onClose())
                bHandled = false;
            else
                endDialog(IDCANCEL);
            return 0;
        }
        virtual    void    updateLayout(int dx, int dy)
        {
        }
    
        VON_MESSAGE(WM_SIZE)
        {
            RECT rect = getWindowRect();
            updateLayout(
                (rect.right - rect.left) - (m_startRect.right - m_startRect.left),
                (rect.bottom - rect.top) - (m_startRect.bottom - m_startRect.top)
            );
            m_startRect = rect;
            return 0;
        }
    
        void    layout(VWnd & wnd, int dx, int dy, bool bLeft, bool bTop, bool bRight, bool bBottom)
        {
            RECT rect;
            wnd.getWindowRect(&rect);
            screenToClient(rect);
    
            if(!bLeft)
                rect.left += dx;
            if(!bTop)
                rect.top += dy;
            if(bRight)
                rect.right += dx;
            if(bBottom)
                rect.bottom += dy;
    
            wnd.move(rect);
        }
    
        void    layout(WORD id, int dx, int dy, bool bLeft, bool bTop, bool bRight, bool bBottom)
        {
            HWND hWnd = GetDlgItem(m_hWnd, id);
            RECT rect;
            GetWindowRect(hWnd, &rect);
            MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rect, 2);
    
            if(!bLeft)
                rect.left += dx;
            if(!bTop)
                rect.top += dy;
            if(bRight)
                rect.right += dx;
            if(bBottom)
                rect.bottom += dy;
    
            MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
        }
    
    
        class iterator
        {
        public:
            iterator(std::map<int, VDlgControl *> & _map, std::map<int, VDlgControl *>::iterator pos)
                : m_map(_map), m_pos(pos)
            {
            }
            iterator(const iterator & o)
                : m_map(o.m_map), m_pos(o.m_pos)
            {
            }
            bool    operator == (iterator p) const { return m_pos == p.m_pos; }
            bool    operator != (iterator p) const { return m_pos != p.m_pos; }
    
            iterator &    operator ++ ()
            {
                VASSERTE(m_pos != m_map.end());
                VDlgControl * pParent = m_pos->second;
                while(++m_pos != m_map.end() && m_pos->second != pParent);
                return *this;
            }
            iterator operator ++ (int)
            {
                iterator tmp = *this;
                ++*this;
                return tmp;
            }
    
        protected:
            std::map<int, VDlgControl *> &    m_map;
            std::map<int, VDlgControl *>::iterator    m_pos;
        };
    
        iterator    begin(VDlgControl * pParent = NULL)
        {
            std::map<int, VDlgControl *>::iterator p = m_controlMap.m_map.begin();
            while(p != m_controlMap.m_map.end() && p->second != pParent)
                ++p;
            return iterator(m_controlMap.m_map, p);
        }
        iterator    end()
        {
            return iterator(m_controlMap.m_map, m_controlMap.m_map.end());
        }
    
        void    preInitDialog()
        {
            for(std::map<int, VDlgControl *>::const_iterator p = m_controlMap.m_map.begin(); p != m_controlMap.m_map.end(); ++p)
            {
                if(p->first != -1)
                {
                    VWnd * wnd = dynamic_cast<VWnd *>(p->second);
                    VRTVERIFYE(wnd);
                    VRTVERIFY(wnd->m_hWnd = GetDlgItem(m_hWnd, p->first), "Invalid resource file");
                    p->second->m_rect = wnd->getRect(m_hWnd);
                }
                else
                    VRTVERIFYE(SetRectEmpty(&p->second->m_rect));
            }
        }
        virtual    void    onInitDialog()
        {
            m_startRect = getWindowRect();
        }
        virtual    LRESULT defWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
        {
            return 0;
        }
    protected:
        RECT    m_startRect;
        static    BOOL CALLBACK dlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
        {
            VContent::ChangecurrentHWND saveCurrentHWND(hWnd);
            if(msg == WM_INITDIALOG)
            {
                VDialog * dlg = (VDialog *)lParam;
                hWnd2ObjMap()[(LPARAM)hWnd] = dlg;
                dlg->m_hWnd = hWnd;
                dlg->preInitDialog();
                dlg->onInitDialog();
            }
            VWnd * win = hWnd2ObjMap()[(LPARAM)hWnd];
            if(win)
            {
                bool bHandled = true;
                LRESULT ret = win->processWindowMessage(VMsgData(hWnd, msg, wParam, lParam), bHandled);
                if(bHandled)
                    win->setWindowLong(DWL_MSGRESULT, (LONG)ret).ignore();
                return bHandled ? TRUE : FALSE;
            }
            return FALSE;
        }
        class InitModeless
        {
        public:
            InitModeless()
            {
                m_hKeyboardFilterHook = SetWindowsHookEx(WH_KEYBOARD, keyboardFilterProc, NULL, GetCurrentThreadId());
                VASSERTE(m_hKeyboardFilterHook);
            }
    
            ~InitModeless()
            {
                UnhookWindowsHookEx(m_hKeyboardFilterHook);
            }
    
            static InitModeless & getInstance()
            {
                static InitModeless m_instance;
                return m_instance;
            }
    
            void    addDialog(HWND dlg)
            {
                m_dlgs.push_back(dlg);
            }
            void    removeDialog(HWND dlg)
            {
                m_dlgs.remove(dlg);
            }
        protected:
            HHOOK            m_hKeyboardFilterHook;
            std::list<HWND>    m_dlgs;
            static LRESULT CALLBACK keyboardFilterProc(int code, WPARAM wParam, LPARAM lParam)
            {
                switch (code)
                {
                case HC_ACTION:
                    {
                        MSG msg;
                        msg.message = (lParam & 0x80000000L) ? WM_KEYUP : WM_KEYDOWN;
                        msg.hwnd = ::GetFocus();
                        msg.wParam = wParam;
                        msg.lParam = lParam;
                        GetCursorPos(&msg.pt);
                        msg.time = 0;
    
                        for(std::list<HWND>::const_iterator p = getInstance().m_dlgs.begin(); p != getInstance().m_dlgs.end(); ++p)
                        {
                            static const TCHAR * IsVlibTmp = TEXT("{0B98B0C3-E0C1-41c4-8E20-528C395EAB6D}");
                            HWND hWnd = *p;
                            if(IsWindowEnabled(hWnd) && !GetProp(hWnd, IsVlibTmp))
                            {
                                SetProp(hWnd, IsVlibTmp, (HANDLE)1);
                                BOOL bRet = IsDialogMessage(hWnd, &msg);
                                SetProp(hWnd, IsVlibTmp, (HANDLE)0);
                                if(bRet)
                                    return (LRESULT)TRUE;
                            }
                        }
                    }
                    break;
                }
                return ::CallNextHookEx(getInstance().m_hKeyboardFilterHook, code, wParam, lParam);
            }
        };
        bool    m_bModeless;
    public:
        VControlMap    m_controlMap;
    };
    
    class VControlWnd : public VWnd
    {
    public:
        virtual    DWORD    getDefStyle()
        {
            return WS_CHILD;
        }
    
        virtual HWND create(HWND hParent, DWORD dwStyle, DWORD dwExStyle, int X, int Y, int nWidth, int nHeight)
        {
            m_hWnd = CreateWindowEx(
                dwExStyle,
                getClassName(),
                NULL,
                dwStyle, 
                X,
                Y,
                nWidth,
                nHeight,
                hParent,
                NULL,
                _ThisModule.m_hModule,
                NULL
            );
            VASSERTE(m_hWnd);
            m_oldWndProc = GetWindowLongPtr(m_hWnd, GWL_WNDPROC);
            SetWindowLongPtr(m_hWnd, GWL_WNDPROC, (LONG_PTR)wndProc);
            hWnd2ObjMap()[(LPARAM)m_hWnd] = this;
            return m_hWnd;
        }
    
        virtual    LRESULT defWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
        {
            return ::CallWindowProc((WNDPROC)m_oldWndProc, hWnd, msg, wParam, lParam);
        }
    protected:
        LONG_PTR    m_oldWndProc;
    
    };
    
    
    #endif//__vwnd_h_


    Пример использования:

    #pragma once
    
    #include <shlwapi.h>
    #include "vwin.h"
    #include "resource.h"
    #include "vcontrols.h"
    #include "vopensavedlg.h"
    #include "veditapp.h"
    #include "vshell.h"
    
    #pragma comment(lib, "shlwapi.lib")
    
    class voptionsdlg : public VDialog
    {
    public:
        voptionsdlg(void);
        ~voptionsdlg(void);
    
        enum { IDD = IDD_OPTIONDLG };
    
        VControl<IDC_ALLLIST, VListView>    m_allPrograms;
    
        virtual    void    onInitDialog()
        {
            m_allPrograms.addColumn(0, TEXT("╚ь "), LVCFMT_LEFT, 100);
            m_allPrograms.addColumn(1, TEXT("╧ЁюуЁрььр"), LVCFMT_LEFT, 400);
    
            std::map<VSysString, VSysString> items;
            load(items);
            for(std::map<VSysString, VSysString>::const_iterator p = items.begin(); p != items.end(); ++p)
                addApp(p->first, p->second);
    
            VDialog::onInitDialog();
        }
    
        void    save()
        {
            std::map<VSysString, VSysString> items;
            int count = m_allPrograms.count();
            for(int i = 0; i < count; ++i)
                items[m_allPrograms.itemText(i, 0)] = m_allPrograms.itemText(i, 1);
            save(items);
        }
    
        virtual    void    updateLayout(int dx, int dy)
        {
            layout(m_allPrograms, dx, dy, true, true, true, true);
            layout(IDOK, dx, dy, false, true, true, false);
            layout(IDCANCEL, dx, dy, false, true, true, false);
    
            layout(IDC_ADDBUTTON, dx, dy, false, true, true, false);
            layout(IDC_EDITBUTTON, dx, dy, false, true, true, false);
            layout(IDC_DELBUTTON, dx, dy, false, true, true, false);
    
            VDialog::updateLayout(dx, dy);
        }
    
    
        VMESSAGE_MAP(voptionsdlg, VDialog);
            VON_COMMAND(IDC_ADDBUTTON)
            {
                veditapp dlg;
                if(dlg.doModal(veditapp::IDD, m_hWnd) == IDOK)
                    addApp(dlg.m_NameStr, dlg.m_FileStr);
                return 0;
            }
            VON_COMMAND(IDC_EDITBUTTON)
            {
                int index = m_allPrograms.cursel();
                if(index >= 0)
                {
                    veditapp dlg;
                    dlg.m_NameStr = m_allPrograms.itemText(index, 0);
                    dlg.m_FileStr = m_allPrograms.itemText(index, 1);
                    if(dlg.doModal(veditapp::IDD, m_hWnd) == IDOK)
                    {
                        m_allPrograms.remove(index);
                        addApp(dlg.m_NameStr, dlg.m_FileStr);
                    }
                }
                return 0;
            }
            VON_COMMAND(IDC_DELBUTTON)
            {
                int index = m_allPrograms.cursel();
                if(index >= 0)
                    m_allPrograms.remove(index);
                return 0;
            }
            VON_NOTIFY(PSN_APPLY)
            {
                save();
                return 0;
            }
            void    addApp(const VSysString & name, const VSysString & file)
            {
                HIMAGELIST hImageList;
                int index = VShell::getIcon(file, hImageList, true);
                if(!m_allPrograms.imageList(LVSIL_SMALL))
                    m_allPrograms.imageList(hImageList, LVSIL_SMALL);
    
                int i = m_allPrograms.add(name, index);
                m_allPrograms.itemText(i, 1, file);
            }
            static    void    rTrim(SYSCHAR * pStr)
            {
                size_t len = vstrlen(pStr);
                while(len && visWhiteSpace(pStr[len - 1]))
                    pStr[len-- - 1] = 0;
            }
            static    void    load(std::map<VSysString, VSysString> & items)
            {
                SYSCHAR buf[MAX_PATH + 1];
                GetModuleFileName(_ThisModule.m_hModule, buf, countof(buf));
                PathRemoveFileSpec(buf);
                PathCombine(buf, buf, TEXT("apps.ini"));
                FILE * f = fopen(buf, "rt");
                if(!f)
                    return;
                while(!feof(f))
                {
                    if(!fgets(buf, countof(buf), f))
                        break;
                    SYSCHAR buf1[MAX_PATH + 1];
                    if(!fgets(buf1, countof(buf1), f))
                        break;
                    rTrim(buf);
                    rTrim(buf1);
    
                    items[buf] = buf1;
                }
                fclose(f);
            }
            static    void    save(std::map<VSysString, VSysString> & items)
            {
                SYSCHAR buf[MAX_PATH + 1];
                GetModuleFileName(_ThisModule.m_hModule, buf, countof(buf));
                PathRemoveFileSpec(buf);
                PathCombine(buf, buf, TEXT("apps.ini"));
                FILE * f = fopen(buf, "wt");
                if(!f)
                    return;
                for(std::map<VSysString, VSysString>::const_iterator p = items.begin(); p != items.end(); ++p)
                {
                    fprintf(f, "%s\n", p->first.c_str());
                    fprintf(f, "%s\n", p->second.c_str());
                }
                fclose(f);
            }
    };
    С уважением Вадим.
    Re[2]: Можно сильнее извратиться....
    От: Рома Мик Россия http://romamik.com
    Дата: 30.06.03 00:06
    Оценка:
    Здравствуйте, lboss, Вы писали:

    L>Но я не могу придумать как форсировать вызов конструктора Initializer'а... Пришлось придумывать MsgResult...

    Я чегой-от никак не пойму: а какого лешего инстанциируются статические члены MsgResult, а конкретно m_Initializer?
    По идее пока соответсвующая OnMsg не вызвана, он не нужен, а вызвана она будет только если ее предварительно зарегистрировать, что делает именно Initializer. Замкнутый круг.
    Ничего не понимаю.

    Копаясь с этим делом я пришел к такому коду ( этот работает ) :
     #include <map>
    
    template < class Param, class Id, Id id >
    struct Message 
    {
        Param &param_;
        Message(Param &param) : param_(param) { }
    };
    
    template < class Target, class Param, class Id >
    struct MessageMap
    {
        typedef bool (*ProxyFun)(Target&, Param&);
    
        template < Id id >
        static bool ConcreteProxyFun(Target &target, Param &param)
        {
            return target.Handle(Message<Param, Id, id>(param));
        }
    
        typedef std::map<Id, ProxyFun> Map;
    
        static Map &GetMap()
        {
            static Map map_;
            return map_;
        }
    
        template < Id id > 
        struct Registrar
        {
            Registrar()
            {
                static struct ActualRegistrar
                {
                    ActualRegistrar()
                    {
                        GetMap()[id] = ConcreteProxyFun<id>;
                    }
                } registrar;
            }
        };
    
        static bool Handle(Target& target, Id id, Param &param)
        {
            Map::iterator iter = GetMap().find(id);
            if(iter == GetMap().end())
                return false;
            else
                return (*iter->second)(target, param);
        }
    
    };

    Использование
    #include <windows.h>
    #include <iostream>
    
    struct MsgParam
    {
        WPARAM wParam_;
        LPARAM lParam_;
        MsgParam(WPARAM wParam, LPARAM lParam) : wParam_(wParam), lParam_(lParam) {}
    };
    
    struct Wnd
    {
    
    /*Wnd() // можно так
        {
            MessageMap< Wnd, MsgParam, UINT >::Registrar< WM_CREATE >();
            MessageMap< Wnd, MsgParam, UINT >::Registrar< WM_COMMAND >();
        }
    */
        MessageMap< Wnd, MsgParam, UINT >::Registrar< WM_CREATE > regCreate; // а можно так
        bool Handle(Message<MsgParam, UINT, WM_CREATE> msg)
        {
            std::cout << "WM_CREATE " << msg.param_.wParam_ << " " << msg.param_.lParam_ << "\n";
            return true;
        }
    
        MessageMap< Wnd, MsgParam, UINT >::Registrar< WM_COMMAND > regCommand; 
        bool Handle(Message<MsgParam, UINT, WM_COMMAND> msg)
        {
            std::cout << "WM_COMMAND " << msg.param_.wParam_ << " " << msg.param_.lParam_ << "\n";
            return true;
        }
    };
    
    int main()
    {
        Wnd wnd;
    
        MessageMap<Wnd, MsgParam, UINT>::Handle(wnd, WM_CREATE, MsgParam(0, 1));
        MessageMap<Wnd, MsgParam, UINT>::Handle(wnd, WM_COMMAND, MsgParam(2, 3));
        MessageMap<Wnd, MsgParam, UINT>::Handle(wnd, WM_CREATE, MsgParam(4, 5));
        std::cin.get();
        return 0;
    }


    Есть ту фсякие косяки, ну да бог с ними, хочется автоматики. А потому пытаемся повторить подвиг lboss'а и... ничего не получается.
    Registrar переименовываем в Result, добавляем ему чуть-чуть и первозим его в глобальное пространство имен:
    template < class Target, class Param, class Id, Id id >
    struct Result
    {
        bool result_;
    
        Result(bool result) : result_(result)
        {
            static struct ActualRegistrar
            {
                ActualRegistrar()
                {
                    MessageMap< Target, Param, Id >::GetMap()[id] = MessageMap< Target, Param, Id >::ConcreteProxyFun<id>;
                }
            } registrar;
        }
    
        operator bool() { return result_; }
    };

    Использование теперь такое:
    struct Wnd
    {
        Result<Wnd, MsgParam, UINT, WM_CREATE> Handle(Message<MsgParam, UINT, WM_CREATE> msg)
        {
            std::cout << "WM_CREATE " << msg.param_.wParam_ << " " << msg.param_.lParam_ << "\n";
            return true;
        }
    
        Result<Wnd, MsgParam, UINT, WM_COMMAND> Handle(Message<MsgParam, UINT, WM_COMMAND> msg)
        {
            std::cout << "WM_COMMAND " << msg.param_.wParam_ << " " << msg.param_.lParam_ << "\n";
            return true;
        }
    };


    Только вот Result::ActualRegistrar не инстанциируется. Да и не должен. Это просто я типа у lboss'а подсмотрел, только чего-то недостмотрел Объясните пожалуйста чего я не догоняю?
    ... << RSDN@Home 1.1 alpha 1 >>
    Re[3]: Можно сильнее извратиться....
    От: Рома Мик Россия http://romamik.com
    Дата: 30.06.03 00:20
    Оценка:
    Здравствуйте, Рома Мик, Вы писали:

    Кстати, а нельзя ли победить синатксис C++ и сделать аналог такой штуки
    template < class Param, Param::Id id >
    struct Message {...}
    
    //А в качестве параметра использовать
    struct MsgParam
    {
        typedef UINT Id;
        ...
    }


    Такое конечно, не компилируется, но вот подобного результата как добиться?
    ... << RSDN@Home 1.1 alpha 1 >>
    Re[4]: Можно сильнее извратиться....
    От: WolfHound  
    Дата: 30.06.03 05:01
    Оценка:
    Здравствуйте, Рома Мик, Вы писали:
    хъ
    РМ>Такое конечно, не компилируется, но вот подобного результата как добиться?
    template < class Param, class Id=Param::Id >
    struct Message {};
    
    struct MsgParam
    {
        typedef UINT Id;
    };

    Ы?
    Пусть это будет просто:
    просто, как только можно,
    но не проще.
    (C) А. Эйнштейн
    Re[5]: Можно сильнее извратиться....
    От: Рома Мик Россия http://romamik.com
    Дата: 30.06.03 09:13
    Оценка:
    Здравствуйте, WolfHound, Вы писали:

    WH>
    WH>template < class Param, class Id=Param::Id >
    WH>struct Message {};
    
    WH>struct MsgParam
    WH>{
    WH>    typedef UINT Id;
    WH>};
    WH>

    WH>Ы?
    Кажется, не Ы.Как минимум так:
    <class Param=Что-то, class Id=Param::Id, Id id>

    И это никак не помогает.
    Если посмотреть на то что я там понаписал, там все время приходится писать нечто такое:
    Message<MsgParam, UINT, WM_CREATE>

    Хотелось бы сократить до такого
    Message<MsgParam, WM_CREATE>

    чтобы UINT вытащился из MsgParam.

    ЗЫ это, впрочем, фигня. Объясните про lboss'овский вариант, пожалуйста.
    ... << RSDN@Home 1.1 alpha 1 >>
    Re[4]: Можно сильнее извратиться....
    От: Рома Мик Россия http://romamik.com
    Дата: 30.06.03 09:22
    Оценка:
    Здравствуйте, Рома Мик, Вы писали:

    РМ>Кстати, а нельзя ли победить синатксис C++ и сделать аналог такой штуки

    Оп, сам догадался.
    template < class Param, typename Param::Id id >
    ... << RSDN@Home 1.1 alpha 1 >>
    Re[3]: Можно сильнее извратиться....
    От: WolfHound  
    Дата: 30.06.03 15:59
    Оценка:
    Здравствуйте, Рома Мик, Вы писали:

    РМ>Я чегой-от никак не пойму: а какого лешего инстанциируются статические члены MsgResult, а конкретно m_Initializer?

    Мой вариант
    template<class T, int ID>
    struct MsgRes
    {
        MsgRes(bool res)
            :res_(res)
        {
            (void)registrator;//А вот и леший :)
        }
        operator bool(){return res_;}
        struct Registrator
        {
            Registrator()
            {
                MessageProcessor<T>::AddHandler<ID>(&Call);
            }
            static bool Call(T* t, WndMsg* msg)
            {
                Msg<ID> msg_(msg);
                return t->OnMessage(msg_);
            }
        };
        static Registrator registrator;
    private:
        bool res_;
    };

    А фокус тут в том что когда обработчик возвращает значение компилятор вынужден инстационировать конструктор который тянет за собой registrator. Как и все гениальное просто.
    ... << RSDN@Home 1.1 alpha 1 >>
    Пусть это будет просто:
    просто, как только можно,
    но не проще.
    (C) А. Эйнштейн
    Re[4]: Можно сильнее извратиться....
    От: Рома Мик Россия http://romamik.com
    Дата: 30.06.03 19:10
    Оценка:
    Здравствуйте, WolfHound, Вы писали:

    Простите непонятливого.

    WH>А фокус тут в том что когда обработчик возвращает значение компилятор вынужден инстационировать конструктор который тянет за собой registrator.

    Только если обработчик не зарегистрировать, он не будет вызван, а следовательно не станет возвращать значение, и как следствие registartor может и не быть инстанциирован. И у меня именно так и происходит.
    Вместо отладки в таком случае неплохо бы, что компилятор генерил отчет о проделанной работе: что он сделал и почему...

    Если не сложно, гляньте на мой код: 3.5 кб
    Там закомментарены конструкторы Window и Wnd ( файлы main.cpp и window.h ). Если их раскомментарить, то все работает как и должно, а в противном случае — нет. Мне же кажется, что трюк я повторил почти дословно...
    ... << RSDN@Home 1.1 alpha 1 >>
    Re[5]: Можно сильнее извратиться....
    От: Рома Мик Россия http://romamik.com
    Дата: 01.07.03 12:33
    Оценка:
    Здравствуйте, Рома Мик, Вы писали:

    РМ>Если не сложно, гляньте на мой код: 3.5 кб

    РМ>Там закомментарены конструкторы Window и Wnd ( файлы main.cpp и window.h ). Если их раскомментарить, то все работает как и должно, а в противном случае — нет. Мне же кажется, что трюк я повторил почти дословно...
    Частично заработало.
    template < class Target, class Param, typename Param::Id id >
    struct MsgResult
    {
        struct Registrar
        {
            Registrar()
            {
                MessageMap< Target, Param >::Register<id>();
            }
    
            static bool ProxyFun(Target &target, Param &param)
            {
                return target.Handle(Message<Param, id>(param));
            }
        };
        static Registrar registrar_;
        bool result_;
        MsgResult(bool result) : result_(result)
        {
            (void)registrar_;
        }
        operator bool() 
        {
            return result_;
        }
    };
    
    
    template < class Target, class Param, typename Param::Id id >
    typename MsgResult< Target, Param, id >::Registrar MsgResult< Target, Param, id >::registrar_;
    

    А без выделенного все компилировалось, но regisrar_ не инстанциировался.
    ... << RSDN@Home 1.1 alpha 1 >>
    Re[4]: По мотивам "Прощания Славянки"
    От: iZEN СССР  
    Дата: 22.08.04 14:20
    Оценка:
    Здравствуйте, c-smile, Вы писали:

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

    CS>И вот в результате вернулся к истокам: http://terra-informatica.org/j-smile
    CS>Получил массу удовольствия имплементируя.

    И где ЭТО сейчас? Напишите пожалуйста.
    Re: По мотивам SWL
    От: Аноним  
    Дата: 25.08.04 11:24
    Оценка:
    Здравствуйте, WolfHound, Вы писали:

    WH>Сейчас наткнулся на SWL

    идея там на 5+ но реализация на 3-


    Я, кстати, ее малость развил — добавил контролов и немного переделал их
    иерархию. Сейчас есть

    SWL::Button
    SWL::Edit
    SWL::Label
    SWL::Combo
    SWL::ListBox
    SWL::ScrollBar
    SWL::CheckBox

    надо кому ?
    Re[2]: По мотивам SWL
    От: _nn_  
    Дата: 25.08.04 11:31
    Оценка:
    Здравствуйте, Аноним, Вы писали:

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


    WH>>Сейчас наткнулся на SWL

    А>идея там на 5+ но реализация на 3-


    А>Я, кстати, ее малость развил — добавил контролов и немного переделал их

    А>иерархию. Сейчас есть

    А> SWL::Button

    А> SWL::Edit
    А> SWL::Label
    А> SWL::Combo
    А> SWL::ListBox
    А> SWL::ScrollBar
    А> SWL::CheckBox

    А>надо кому ?


    Вы выложите ее, а потом люди скажут надо или нет
    http://rsdn.nemerleweb.com
    http://nemerleweb.com
    Re[3]: По мотивам SWL
    От: Аноним  
    Дата: 25.08.04 11:57
    Оценка:
    Здравствуйте, _nn_, Вы писали:


    __>Вы выложите ее, а потом люди скажут надо или нет



    Куда ?
    Re[4]: По мотивам SWL
    От: _nn_  
    Дата: 25.08.04 11:59
    Оценка:
    Здравствуйте, Аноним, Вы писали:

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



    __>>Вы выложите ее, а потом люди скажут надо или нет



    А>Куда ?

    На RSDN конечно.

    Положите в разделе файлы и дайте ссылку.
    И ждите комментариев
    http://rsdn.nemerleweb.com
    http://nemerleweb.com
    Re[5]: По мотивам "Прощания Славянки"
    От: vdimas Россия  
    Дата: 01.09.04 23:07
    Оценка:
    Здравствуйте, limax, Вы писали:

    CS>> Хотя в твоём варианте с on(mouse_event&) эта проблема вроде исчезает, однако, по-моему, появляется другая: при добавлении в дочернем классе одной новой функции on(death_for_all&) компилятор прячет все старые on(...).


    Виртуальные ф-ии не спрячешь. Ничто не помешает их переопределять в последующих потомках,
    или вызывать внутри либы из базового класса. А учитывая, что, по-хорошему, все эти ф-ии являются protected, так о чем, вообще, речь???

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

    Кстати, твой подход абсолютно стольк же монолитен, только требует в 10 раз больше монолитного кода
    При смене имени классов или сообщений тебе точно так же придется везде все поменять...

    L>Давай закроем тему? Это бесполезный спор. Каждый останется при своём мнении и будет пользовать тем, что ему удобнее. Тем более, что я не хочу яростно защищать ни один из существующих методов обработки сообщений — у каждого своих недостатков предостаточно.

    В принципе, области применения очевидны.
    Если тебуется перехватывать маленькое число сообщений в миниатюрном приложении, то подход с шаблонами рулит,
    если же идет "тяжелая артиллерия", с разработкой насыщенных контролов или множества форм прикладного уровня, то вариант с виртуальными ф-иями явно предпочтительней.
    Re[2]: По мотивам SWL
    От: Kluev  
    Дата: 02.09.04 08:03
    Оценка:
    Здравствуйте, Аноним, Вы писали:

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


    WH>>Сейчас наткнулся на SWL

    А>идея там на 5+ но реализация на 3-


    А>Я, кстати, ее малость развил — добавил контролов и немного переделал их

    А>иерархию. Сейчас есть

    А> SWL::Button

    А> SWL::Edit
    А> SWL::Label
    А> SWL::Combo
    А> SWL::ListBox
    А> SWL::ScrollBar
    А> SWL::CheckBox

    А>надо кому ?


    Давайте выложим как добавку к основной статье. Напишите Odi$$ey-у на odissey [at] rsdn [dot]ru Он выкладывал SWL на сайт.
     
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.