Указатели на функции-члены и наследование

Поэтому я работаю над простой оболочкой win32 для собственного удобства и столкнулся с несколько сложной проблемой.

у этого есть много других участников, но я немного опускаю и оставляю только оскорбляющих участников.

class Windows::AbstractWindow
{
public:
     void InstallHandler(UINT msgName, void (Windows::AbstractWindow::*)(HWND, UINT, WPARAM, LPARAM));

private:
     std::map<UINT, void (Windows::AbstractWindow::*)(HWND, UINT, WPARAM, LPARAM)> HandlerIndex;

};

(для записи, в данном случае Windows - это пространство имен различных классов и объектов, которые я создал)

просто немного противно, но позвольте мне объяснить мой процесс и рассуждения. у меня есть класс с именем AbstractWindow, который содержит больше всего функциональных возможностей окна в очень объектно-ориентированном виде.

Сейчас я работаю над способом получения закрытых функций-членов и сохранения их на карте с помощью указателей на них, которые определяются сообщением Windows, которое они должны обрабатывать. Таким образом, когда сообщение получено процедурой Windows, оно просматривает эту карту, чтобы увидеть, установлен ли для него обработчик. Если он есть, он вызывает эту функцию и завершает работу. Он не вызывает DefWindowProc и завершает работу. достаточно просто.

Однако этот объект никогда не должен создаваться, а просто наследоваться и расширяться. Проблема в том, что объявление указателя функции карты имеет тип AbstractWindow, который не позволяет мне хранить указатели на функции-члены типа, унаследованного от AbstractWindow. Например,

class BasicWindow : public Windows::AbstractWindow
{
public:
    BasicWindow() 
    {
         InstallHandler(WM_CREATE, &create);
    }

private:
    void Create(HWND, UINT, WPARAM, LPARAM) {}
}

... выдает ошибку:

error C2664: 'Windows::AbstractWindow::InstallHandler' : cannot convert parameter 2 from 'void (__thiscall BasicWindow::* )(HWND,MSG,WPARAM,LPARAM)' to 'void (__thiscall Windows::AbstractWindow::* )(HWND,UINT,WPARAM,LPARAM)'

потому что типы указателей не совпадают, несмотря на то, что они унаследованы от базового класса. Так кто-нибудь хочет предложить решение при сохранении этого метода? И если нет, я также открыт для предложений, которые, по вашему мнению, сделают обработку сообщений более удобной, чем этот способ.

3 ответа

Решение

Вам следует ознакомиться с шаблоном шаблонов с любопытной повторяемостью ( http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern).

По сути, вы превращаете свой базовый класс в шаблон с параметром шаблона, который задает подкласс.

Возможно, вам будет легче понять, если вы увидите это:

namespace Windows
{
     template <typename T>
     class AbstractWindow
     {
     public:
          void InstallHandler(UINT msgName, void (T::*)(HWND, UINT, WPARAM, LPARAM));

     private:
          std::map<UINT, void (T::*)(HWND, UINT, WPARAM, LPARAM)> HandlerIndex;

     };
}

class BasicWindow : public Windows::AbstractWindow< BasicWindow >
{
public:
    BasicWindow() 
    {
         InstallHandler(WM_CREATE, &BasicWindow::Create);
    }

private:
    void Create(HWND, UINT, WPARAM, LPARAM) {}
};

Проблема, с которой вы сталкиваетесь, заключается в том, что вы пытаетесь сделать обращение противоположным образом. Функциональные указатели контравариантны в своих this аргументы (т. е. указатель на функцию из базового класса функция будет делать для указателя на метод производного класса, а не наоборот). Вы можете:

  • просто бросил (с static_cast, так как это обратное неявное преобразование). Он будет работать нормально, если вы можете быть уверены, что никогда не вызываете метод в неподходящем классе (например, NotABasicWindow().*method()).
  • избавиться от схемы и зарегистрировать общие функции (т. е. все, что можно вызывать вместо указателей на функции-члены). Вы бы использовали, например. std::function<void(HWND, UINT, WPARAM, LPARAM)> как ваш тип обработчика и регистрирует обработчики, которые будут знать их окно (например, лямбда-функции).

    • Лямбда-функции являются функцией C++ 11. Они примерно соответствуют функциональным объектам со связующими; они создают анонимную функцию, которая может ссылаться на переменные из области, в которой они находятся. Примером будет

      [=](HWND wnd, UINT i, WPARAM wp, LPARAM lp) { this->printHandler(wnd, i, wp, lp); }
      

      который бы запомнил thisтак что бы позвонить printHandler для текущего объекта (текущий код, создающий лямбду, а не вызывающий код) при вызове. Конечно, объект, для которого должен быть вызван метод, может быть просто еще одним параметром.

      Лямбды, а также другие функциональные объекты (т.е. объекты, которые имеют operator() определены) могут быть преобразованы и сохранены как std::function объекты.

Будет ли что-то подобное работать...? Ps. Я бы использовал typedef для подписи функции.

BasicWindow() 
    {
         InstallHandler(WM_CREATE, reinterpret_cast<void (__thiscall Windows::AbstractWindow::* )(HWND,UINT,WPARAM,LPARAM)>(&create));
    }
Другие вопросы по тегам