Singleton für Anfänger.
Verfasst: 09.09.2011, 20:00
Hallo zusammen.
Nun ich kämpfe mich gerade durch die Welt von C++ da ich aus purer Neugierde einfach mal eine kleine simple DirectX 11 Anwendung erstellen möchte. Tja, sehr weit bin ich noch nicht gekommen, da stellt sich doch schon mal das erste Problem. Ich möchte eine simple Logdatei verwenden, die von den verschiedenen Komponenten verwendet werden kann, ohne immer eine Instanz übergeben zu müssen. Meine aktuelle Implementation ist, naja, unschön und sollte sicher auch nicht so implementiert werden. In diversen Tutorials wird dabei ein Singleton-Konzept verwendet. Nun aber ist es ja so, das durchaus mehere Instanzen meiner Loggerklasse erstellt werden können. Warum also ein Singleton verwenden? Aber zuerst mal der Code:
Wie könnte ich meine Logdatei implementieren?
Besten Dank schon mal im Voraus
Nun ich kämpfe mich gerade durch die Welt von C++ da ich aus purer Neugierde einfach mal eine kleine simple DirectX 11 Anwendung erstellen möchte. Tja, sehr weit bin ich noch nicht gekommen, da stellt sich doch schon mal das erste Problem. Ich möchte eine simple Logdatei verwenden, die von den verschiedenen Komponenten verwendet werden kann, ohne immer eine Instanz übergeben zu müssen. Meine aktuelle Implementation ist, naja, unschön und sollte sicher auch nicht so implementiert werden. In diversen Tutorials wird dabei ein Singleton-Konzept verwendet. Nun aber ist es ja so, das durchaus mehere Instanzen meiner Loggerklasse erstellt werden können. Warum also ein Singleton verwenden? Aber zuerst mal der Code:
Code: Alles auswählen
#define WIN32_LEAN_AND_MEAN
#include <ctime>
#include <exception>
#include <stdexcept>
#include <fstream>
#include <string>
#include <iomanip>
#include <Windows.h>
namespace Tutorial {
///////////////////////////////////////////////////////////////////////////////
class Uncopyable
{
protected:
Uncopyable() {}
~Uncopyable() {}
private:
Uncopyable(const Uncopyable&);
Uncopyable& operator=(const Uncopyable&);
};
///////////////////////////////////////////////////////////////////////////////
class CriticalSection : protected Uncopyable
{
public:
CriticalSection();
~CriticalSection();
void Enter();
bool TryEnter();
void Leave();
private:
CRITICAL_SECTION mSection;
};
///////////////////////////////////////////////////////////////////////////////
CriticalSection::CriticalSection()
{
InitializeCriticalSection(&mSection);
}
///////////////////////////////////////////////////////////////////////////////
CriticalSection::~CriticalSection()
{
LeaveCriticalSection(&mSection);
DeleteCriticalSection(&mSection);
}
///////////////////////////////////////////////////////////////////////////////
void CriticalSection::Enter()
{
EnterCriticalSection(&mSection);
}
///////////////////////////////////////////////////////////////////////////////
bool CriticalSection::TryEnter()
{
return TryEnterCriticalSection(&mSection) == TRUE;
}
///////////////////////////////////////////////////////////////////////////////
void CriticalSection::Leave()
{
LeaveCriticalSection(&mSection);
}
///////////////////////////////////////////////////////////////////////////////
class Logger : protected Uncopyable
{
public:
explicit Logger(const std::string& filename);
~Logger();
void WriteLine(const std::string& message);
private:
std::ofstream mStream;
};
///////////////////////////////////////////////////////////////////////////////
Logger::Logger(const std::string& filename) : mStream(filename)
{
if (mStream.fail())
{
throw std::runtime_error("Failed to create logilfe!");
}
WriteLine("Logger created");
}
///////////////////////////////////////////////////////////////////////////////
Logger::~Logger()
{
WriteLine("Logger destroyed");
}
///////////////////////////////////////////////////////////////////////////////
void Logger::WriteLine(const std::string& message)
{
__time64_t localTime;
_time64(&localTime);
struct tm time;
_localtime64_s(&time, &localTime);
mStream
<< std::setfill('0')
<< std::setw(2)
<< time.tm_mon
<< "."
<< std::setw(2)
<< time.tm_mday
<< "."
<< time.tm_year + 1900
<< "/"
<< std::setw(2)
<< time.tm_hour
<< ":"
<< std::setw(2)
<< time.tm_min
<< ":"
<< std::setw(2)
<< time.tm_sec
<< " - "
<< message
<< std::endl;
}
///////////////////////////////////////////////////////////////////////////////
void Logfile(const std::string& message)
{
static CriticalSection section;
static Logger logger("Logfile.txt");
section.Enter();
logger.WriteLine(message);
section.Leave();
}
///////////////////////////////////////////////////////////////////////////////
void InvalidArgument(const std::string& message)
{
Logfile(std::string("Invalid argument: ") + message);
throw std::invalid_argument(message.c_str());
}
///////////////////////////////////////////////////////////////////////////////
void RuntimeError(const std::string& message)
{
Logfile(std::string("Runtime error: ") + message);
throw std::runtime_error(message.c_str());
}
///////////////////////////////////////////////////////////////////////////////
struct MessageInfo
{
public:
MessageInfo();
public:
HWND Window;
UINT Message;
WPARAM Param1;
LPARAM Param2;
};
///////////////////////////////////////////////////////////////////////////////
MessageInfo::MessageInfo() : Window(nullptr), Message(0), Param1(0), Param2(0) {}
///////////////////////////////////////////////////////////////////////////////
class WindowClass : protected Uncopyable
{
protected:
explicit WindowClass(const HINSTANCE instance);
virtual ~WindowClass();
virtual bool MessageHandler(MessageInfo&) { return true; }
private:
static LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM param1, LPARAM param2);
protected:
const HINSTANCE mInstance;
};
///////////////////////////////////////////////////////////////////////////////
WindowClass::WindowClass(const HINSTANCE instance) : mInstance(instance)
{
if (!instance)
{
InvalidArgument("Invalid instance passed!");
}
WNDCLASSEX desc;
desc.cbSize = sizeof(desc);
desc.cbClsExtra = 0;
desc.cbWndExtra = 0;
desc.hbrBackground = reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
desc.hCursor = LoadCursorW(nullptr, IDC_ARROW);
desc.hIcon = LoadIconW(nullptr, IDI_APPLICATION);
desc.hIconSm = desc.hIcon;
desc.hInstance = mInstance;
desc.lpfnWndProc = &WindowProc;
desc.lpszClassName = L"WindowClass";
desc.lpszMenuName = nullptr;
desc.style = CS_DBLCLKS;
if (!RegisterClassExW(&desc))
{
RuntimeError("Failed to register window class!");
}
}
///////////////////////////////////////////////////////////////////////////////
WindowClass::~WindowClass()
{
UnregisterClassW(L"WindowClass", mInstance);
}
///////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK WindowClass::WindowProc(HWND window, UINT message, WPARAM param1, LPARAM param2)
{
// Die beim erstellen des Fensters übergebene Fensterklasseninstanz
// speichern damit Nachrichten an diese weitergeleitet werden können.
if (message == WM_NCCREATE)
{
SetWindowLongPtrW(
window,
GWL_USERDATA,
reinterpret_cast<long>(
reinterpret_cast<WindowClass*>(
reinterpret_cast<LPCREATESTRUCTW>(param2)->lpCreateParams)));
}
auto callback(GetWindowLongPtrW(window, GWL_USERDATA));
auto process(true);
if (callback)
{
MessageInfo info;
info.Window = window;
info.Message = message;
info.Param1 = param1;
info.Param2 = param2;
try
{
process = reinterpret_cast<WindowClass*>(callback)->MessageHandler(info);
}
catch (...)
{
// Keine Ausnahmen zulassen.
}
}
return process ? DefWindowProcW(window, message, param1, param2) : 0;
}
///////////////////////////////////////////////////////////////////////////////
class Window : protected WindowClass
{
protected:
explicit Window(const HINSTANCE instance);
virtual ~Window();
void SetClientSize(UINT width, UINT height);
void CenterAtNearestWorkArea();
protected:
HWND mWindow;
};
///////////////////////////////////////////////////////////////////////////////
Window::Window(const HINSTANCE instance) : WindowClass(instance)
{
// Dem Fenster die Fensterklasseninstanz übergeben damit eine abgeleitete
// Anwendung Nachrichten verarbeiten kann.
mWindow = CreateWindowW(
L"WindowClass",
nullptr,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
nullptr,
nullptr,
mInstance,
reinterpret_cast<void*>(this));
if (!mWindow)
{
RuntimeError("Failed to create window!");
}
Logfile("Window created");
}
///////////////////////////////////////////////////////////////////////////////
Window::~Window()
{
DestroyWindow(mWindow);
Logfile("Window destroyed");
}
///////////////////////////////////////////////////////////////////////////////
void Window::SetClientSize(UINT width, UINT height)
{
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = static_cast<long>(width);
rect.bottom = static_cast<long>(height);
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
SetWindowPos(
mWindow,
nullptr,
0,
0,
static_cast<int>(rect.right - rect.left),
static_cast<int>(rect.bottom - rect.top),
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
}
///////////////////////////////////////////////////////////////////////////////
void Window::CenterAtNearestWorkArea()
{
RECT rect;
GetWindowRect(mWindow, &rect);
const auto width(rect.right - rect.left);
const auto height(rect.bottom - rect.top);
MONITORINFO monitor;
monitor.cbSize = sizeof(monitor);
GetMonitorInfoW(MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST), &monitor);
SetWindowPos(
mWindow,
nullptr,
static_cast<int>((monitor.rcWork.right - width) / 2),
static_cast<int>((monitor.rcWork.bottom - height) / 2),
0,
0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}
///////////////////////////////////////////////////////////////////////////////
class Application : protected Window
{
public:
explicit Application(const HINSTANCE instance);
void Update();
private:
bool MessageHandler(MessageInfo& info);
};
///////////////////////////////////////////////////////////////////////////////
Application::Application(const HINSTANCE instance) : Window(instance)
{
SetClientSize(800, 600);
SetWindowTextW(mWindow, L"Tutorial");
CenterAtNearestWorkArea();
ShowWindow(mWindow, SW_NORMAL);
}
///////////////////////////////////////////////////////////////////////////////
void Application::Update()
{
// Irgendwas machen ...
}
///////////////////////////////////////////////////////////////////////////////
bool Application::MessageHandler(MessageInfo& info)
{
bool process(true);
switch (info.Message)
{
case WM_CLOSE:
// Nicht weiterverarbeiten da anonsten die DestroyWindow Funktion
// aufgerufen werden würde. Stattdessen eine WM_QUIT Nachricht
// ablegen damit die Nachrichtenschleife verlassen werden kann.
process = false;
PostQuitMessage(0);
break;
}
return process;
}
} // namespace Tutorial
///////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE instance, HINSTANCE, char*, int)
{
auto succeeded(false);
try
{
Tutorial::Application application(instance);
MSG message;
message.message = 0;
while (message.message != WM_QUIT)
{
// Alle verarbeiteten Nachrichten anschliessend entfernen.
if (PeekMessageW(&message, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&message);
DispatchMessageW(&message);
}
else
{
application.Update();
}
}
succeeded = true;
}
catch (const std::exception& handler)
{
MessageBoxA(nullptr, handler.what(), "Exception", MB_ICONERROR);
}
return succeeded ? 0 : -1;
}
Besten Dank schon mal im Voraus