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, и я думаю, что вы ничего не можете с этим поделать.