C++ Передача личных данных из непрозрачного указателя

Недавно я узнал об непрозрачных указателях в C++. Я начал использовать их, чтобы скрыть частных участников, которые являются платформенными. Такие как ссылки на определения в <windows.h> и т.п.

Теперь у меня есть несколько систем, которые строят друг друга и должны общаться. Например, Direct3D нужен дескриптор окна (HWND). Я не хочу предоставлять определения платформы моей базовой системе, однако мои подсистемы должны передавать эти данные.

Я выставляю непрозрачные данные и разрешаю доступ через пустой указатель. Это позволяет получить доступ ко всем частным данным.

Пример использования (main.cpp):

// System:: namespace is my platform specific code
System::Window window;
System::DirectX::Direct3D9 gfxcontext(window);

Определение окна (System/Window.h):

class Window
{
    WindowData* data; // Opaque pointer
public:
    void* getHandle() const; // returns an HWND handle
    Window();
    ~Window();
}

Как получить полезные данные (Direct3D9.cpp):

#include "Window.h"

Direct3D9::Direct3D9(const Window& _window)
{
    HWND windowHandle = *(HWND*)_window.getHandle();
    // [...]
    pp.hDeviceWindow = windowHandle;
}

Однако этот код работает!

*(HWND*)_window.getHandle() = 0; // changes HWND in WindowData to NULL!

Есть ли способ передавать информацию о платформе между подсистемами, не подвергая ее независимому коду и сохраняя конфиденциальность личных данных?


Изменить текущую реализацию WindowData

struct Window::WindowData
{
    static LRESULT CALLBACK MessageHandler(HWND, UINT, WPARAM, LPARAM);
    HWND windowHandle;
    WNDCLASSEX windowClass;
    HINSTANCE processInstance;
};

HWND используется DirectX в параметрах представления (D3DPRESENT_PARAMETERS::hDeviceWindow)

3 ответа

Решение

Я бы позволил getHandle (или лучше getWindowData вернуть WindowData * вместо void *, Тогда пусть WindowData быть просто предварительным объявлением в файле "System/Window.h".

Внутри "Direct3D9" используйте полное определение WindowData, так:

HWND hwnd = _window.getWindowData()->windowHandle;

Если на более позднем этапе вы портируете на Linux, вы можете иметь совершенно иную структуру внутри WindowData [основано на некоторых #ifdef __WIN32/#else тип структуры на стороне реализации.

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

например:

struct WindowHandleImpl
{
  virtual void show() = 0;
  virtual void maximize() = 0;
  //etc...
};

struct Win32WinHandleImpl : WindowHandleImpl
{
  std::unique_ptr<HWND> handle_; //Use deleter...
  virtual void show(); //In terms of HWND, using Win32 API
  virtual void maximize();
};

struct XWinHandleImpl : WindowHandleImpl
{
  //In terms of platform specific handle.
};

struct Window
{
  void show(); //In terms of WindowHandleImpl
  void maximize();//In terms of WindowHandleImpl
  private:
    std::unique_ptr<WindowHandleImpl> pimpl_;
};

Window::Window( const Factory& factory )
: pimpl_( factory.createWindow() )
{
}
//or 
Window::Window()
: pimpl_( SystemFactory::instance().createWindow() )
{
}

Вы можете скопировать данные и вернуть unique_ptr. Или вы можете просто вернуть HWND как void* вместо HWND*, так как в любом случае это просто указатель, хотя это действительно использует реализацию. Имейте в виду, однако, что другие все же могут каким-то образом изменить ваше окно через HWND, и я думаю, что вы ничего не можете с этим поделать.

Другие вопросы по тегам